1 /******************************************************************************
2 *
3 * Copyright © International Business Machines Corp., 2006-2008
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * NAME
20 * librttest.c
21 *
22 * DESCRIPTION
23 * A set of commonly used convenience functions for writing
24 * threaded realtime test cases.
25 *
26 * USAGE:
27 * To be included in testcases.
28 *
29 * AUTHOR
30 * Darren Hart <dvhltc@us.ibm.com>
31 *
32 * HISTORY
33 * 2006-Apr-26: Initial version by Darren Hart
34 * 2006-May-08: Added atomic_{inc,set,get}, thread struct, debug function,
35 * rt_init, buffered printing -- Vernon Mauery
36 * 2006-May-09: improved command line argument handling
37 * 2007-Jul-12: Added latency tracing functions and I/O helper functions
38 * -- Josh triplett
39 * 2008-Jan-10: Added RR thread support to tests -- Chirag Jog
40 *
41 *****************************************************************************/
42
43 #include <librttest.h>
44 #include <libstats.h>
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <signal.h>
49 #include <time.h>
50 #include <string.h>
51 #include <pthread.h>
52 #include <sched.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <getopt.h>
56 #include <sys/prctl.h>
57 #include <sys/stat.h>
58 #include <sys/syscall.h>
59 #include <sys/types.h>
60 #include <sys/mman.h>
61 #include <fcntl.h>
62 #include <math.h>
63
64 static LIST_HEAD(_threads);
65 static atomic_t _thread_count = { -1 };
66 static unsigned long iters_per_us;
67
68 pthread_mutex_t _buffer_mutex;
69 char *_print_buffer = NULL;
70 int _print_buffer_offset = 0;
71 int _dbg_lvl = 0;
72 double pass_criteria;
73
74 static int _use_pi = 1;
75
76 /* function implementations */
rt_help(void)77 void rt_help(void)
78 {
79 printf("librt standard options:\n");
80 printf
81 (" -b(0,1) 1:enable buffered output, 0:diable buffered output\n");
82 printf(" -p(0,1) 0:don't use pi mutexes, 1:use pi mutexes\n");
83 printf(" -m use mlockall\n");
84 printf
85 (" -v[0-4] 0:no debug, 1:DBG_ERR, 2:DBG_WARN, 3:DBG_INFO, 4:DBG_DEBUG\n");
86 printf(" -s Enable saving stats data (default disabled)\n");
87 printf(" -c Set pass criteria\n");
88 }
89
90 /* Calibrate the busy work loop */
calibrate_busyloop(void)91 void calibrate_busyloop(void)
92 {
93 volatile int i = CALIBRATE_LOOPS;
94 nsec_t start, end;
95
96 start = rt_gettime();
97 while (--i > 0) {
98 continue;
99 }
100 end = rt_gettime();
101
102 iters_per_us = (CALIBRATE_LOOPS * NS_PER_US) / (end - start);
103 }
104
rt_init_long(const char * options,const struct option * longopts,int (* parse_arg)(int option,char * value),int argc,char * argv[])105 int rt_init_long(const char *options, const struct option *longopts,
106 int (*parse_arg) (int option, char *value), int argc,
107 char *argv[])
108 {
109 const struct option *cur_opt;
110 int use_buffer = 1;
111 char *longopt_vals;
112 size_t i;
113 int c;
114 opterr = 0;
115 int mlock = 0;
116 char *all_options;
117
118 if (asprintf(&all_options, ":b:mp:v:sc:%s", options) == -1) {
119 fprintf(stderr,
120 "Failed to allocate string for option string\n");
121 exit(1);
122 }
123
124 /* Check for duplicate options in optstring */
125 for (i = 0; i < strlen(all_options); i++) {
126 char opt = all_options[i];
127
128 if (opt == ':')
129 continue;
130
131 /* Search ahead */
132 if (strchr(&all_options[i + 1], opt)) {
133 fprintf(stderr,
134 "Programmer error -- argument -%c already used at least twice\n",
135 opt);
136 exit(1);
137 }
138 }
139
140 /* Ensure each long options has a known unique short option in val. */
141 longopt_vals = "";
142 cur_opt = longopts;
143 while (cur_opt && cur_opt->name) {
144 if (cur_opt->flag) {
145 fprintf(stderr, "Programmer error -- argument --%s flag"
146 " is non-null\n", cur_opt->name);
147 exit(1);
148 }
149 if (!strchr(all_options, cur_opt->val)) {
150 fprintf(stderr, "Programmer error -- argument --%s "
151 "shortopt -%c wasn't listed in options (%s)\n",
152 cur_opt->name, cur_opt->val, all_options);
153 exit(1);
154 }
155 if (strchr(longopt_vals, cur_opt->val)) {
156 fprintf(stderr, "Programmer error -- argument --%s "
157 "shortopt -%c is used more than once\n",
158 cur_opt->name, cur_opt->val);
159 exit(1);
160 }
161 if (asprintf(&longopt_vals, "%s%c", longopt_vals, cur_opt->val)
162 < 0) {
163 perror("asprintf");
164 exit(2);
165 }
166 cur_opt++;
167 }
168
169 while ((c = getopt_long(argc, argv, all_options, longopts, NULL)) != -1) {
170 switch (c) {
171 case 'c':
172 pass_criteria = atof(optarg);
173 break;
174 case 'b':
175 use_buffer = atoi(optarg);
176 break;
177 case 'p':
178 _use_pi = atoi(optarg);
179 break;
180 case 'm':
181 mlock = 1;
182 break;
183 case 'v':
184 _dbg_lvl = atoi(optarg);
185 break;
186 case 's':
187 save_stats = 1;
188 break;
189 case ':':
190 if (optopt == '-')
191 fprintf(stderr, "long option missing arg\n");
192 else
193 fprintf(stderr, "option -%c: missing arg\n",
194 optopt);
195 parse_arg('h', optarg); /* Just to display usage */
196 exit(1); /* Just in case. (should normally be done by usage()) */
197 case '?':
198 if (optopt == '-')
199 fprintf(stderr, "unrecognized long option\n");
200 else
201 fprintf(stderr, "option -%c not recognized\n",
202 optopt);
203 parse_arg('h', optarg); /* Just to display usage */
204 exit(1); /* Just in case. (should normally be done by usage()) */
205 default:
206 if (parse_arg && parse_arg(c, optarg))
207 break; /* Application option */
208
209 fprintf(stderr,
210 "Programmer error -- option -%c defined but not handled\n",
211 c);
212 exit(1);
213 }
214 }
215 if (!_use_pi)
216 printf
217 ("Priority Inheritance has been disabled for this run.\n");
218 if (use_buffer)
219 buffer_init();
220 if (mlock) {
221 if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
222 perror("failed to lock memory\n");
223 exit(1);
224 }
225 }
226
227 calibrate_busyloop();
228
229 /*
230 * atexit() order matters here - buffer_print() will be called before
231 * buffer_fini().
232 */
233 atexit(buffer_fini);
234 atexit(buffer_print);
235 return 0;
236 }
237
rt_init(const char * options,int (* parse_arg)(int option,char * value),int argc,char * argv[])238 int rt_init(const char *options, int (*parse_arg) (int option, char *value),
239 int argc, char *argv[])
240 {
241 return rt_init_long(options, NULL, parse_arg, argc, argv);
242 }
243
buffer_init(void)244 void buffer_init(void)
245 {
246 _print_buffer = malloc(PRINT_BUFFER_SIZE);
247 if (!_print_buffer)
248 fprintf(stderr,
249 "insufficient memory for print buffer - printing directly to stderr\n");
250 else
251 memset(_print_buffer, 0, PRINT_BUFFER_SIZE);
252 }
253
buffer_print(void)254 void buffer_print(void)
255 {
256 if (_print_buffer) {
257 fprintf(stderr, "%s", _print_buffer);
258 memset(_print_buffer, 0, PRINT_BUFFER_SIZE);
259 _print_buffer_offset = 0;
260 }
261 }
262
buffer_fini(void)263 void buffer_fini(void)
264 {
265 if (_print_buffer)
266 free(_print_buffer);
267 _print_buffer = NULL;
268 }
269
cleanup(int i)270 void cleanup(int i)
271 {
272 printf("Test terminated with asynchronous signal\n");
273 buffer_print();
274 buffer_fini();
275 if (i)
276 exit(i);
277 }
278
setup()279 void setup()
280 {
281 signal(SIGINT, cleanup);
282 signal(SIGQUIT, cleanup);
283 signal(SIGTERM, cleanup);
284 }
285
create_thread(void * (* func)(void *),void * arg,int prio,int policy)286 int create_thread(void *(*func) (void *), void *arg, int prio, int policy)
287 {
288 struct sched_param param;
289 int id, ret;
290 struct thread *thread;
291
292 id = atomic_inc(&_thread_count);
293
294 thread = malloc(sizeof(struct thread));
295 if (!thread)
296 return -1;
297
298 list_add_tail(&thread->_threads, &_threads);
299 pthread_cond_init(&thread->cond, NULL); // Accept the defaults
300 init_pi_mutex(&thread->mutex);
301 thread->id = id;
302 thread->priority = prio;
303 thread->policy = policy;
304 thread->flags = 0;
305 thread->arg = arg;
306 thread->func = func;
307 param.sched_priority = prio;
308
309 pthread_attr_init(&thread->attr);
310 pthread_attr_setinheritsched(&thread->attr, PTHREAD_EXPLICIT_SCHED);
311 pthread_attr_setschedpolicy(&thread->attr, thread->policy);
312 pthread_attr_setschedparam(&thread->attr, ¶m);
313
314 if ((ret =
315 pthread_create(&thread->pthread, &thread->attr, func,
316 (void *)thread))) {
317 printf("pthread_create failed: %d (%s)\n", ret, strerror(ret));
318 list_del(&thread->_threads);
319 pthread_attr_destroy(&thread->attr);
320 free(thread);
321 return -1;
322 }
323 pthread_attr_destroy(&thread->attr);
324
325 return id;
326 }
327
create_fifo_thread(void * (* func)(void *),void * arg,int prio)328 int create_fifo_thread(void *(*func) (void *), void *arg, int prio)
329 {
330 return create_thread(func, arg, prio, SCHED_FIFO);
331 }
332
create_rr_thread(void * (* func)(void *),void * arg,int prio)333 int create_rr_thread(void *(*func) (void *), void *arg, int prio)
334 {
335 return create_thread(func, arg, prio, SCHED_RR);
336 }
337
create_other_thread(void * (* func)(void *),void * arg)338 int create_other_thread(void *(*func) (void *), void *arg)
339 {
340 return create_thread(func, arg, 0, SCHED_OTHER);
341 }
342
set_thread_priority(pthread_t pthread,int prio)343 int set_thread_priority(pthread_t pthread, int prio)
344 {
345 struct sched_param sched_param;
346 sched_param.sched_priority = prio;
347 int policy;
348
349 policy = (prio > 0) ? SCHED_FIFO : SCHED_OTHER;
350
351 return pthread_setschedparam(pthread, policy, &sched_param);
352 }
353
set_priority(int prio)354 int set_priority(int prio)
355 {
356 struct sched_param sp;
357 int ret = 0;
358
359 sp.sched_priority = prio;
360 if (sched_setscheduler(0, SCHED_FIFO, &sp) != 0) {
361 perror("sched_setscheduler");
362 ret = -1;
363 }
364 return ret;
365 }
366
join_thread(int i)367 void join_thread(int i)
368 {
369 struct thread *p, *t = NULL;
370 list_for_each_entry(p, &_threads, _threads) {
371 if (p->id == i) {
372 t = p;
373 break;
374 }
375 }
376 if (t) {
377 t->flags |= THREAD_QUIT;
378 if (t->pthread)
379 pthread_join(t->pthread, NULL);
380 list_del(&t->_threads);
381 }
382 }
383
all_threads_quit(void)384 void all_threads_quit(void)
385 {
386 struct thread *p;
387 list_for_each_entry(p, &_threads, _threads) {
388 p->flags |= THREAD_QUIT;
389 }
390 }
391
join_threads(void)392 void join_threads(void)
393 {
394 all_threads_quit();
395 struct thread *p, *t;
396 list_for_each_entry_safe(p, t, &_threads, _threads) {
397 if (p->pthread)
398 pthread_join(p->pthread, NULL);
399 list_del(&p->_threads);
400 }
401 }
402
get_thread(int i)403 struct thread *get_thread(int i)
404 {
405 struct thread *p;
406 list_for_each_entry(p, &_threads, _threads) {
407 if (p->id == i) {
408 return p;
409 }
410 }
411 return NULL;
412 }
413
ts_minus(struct timespec * ts_end,struct timespec * ts_start,struct timespec * ts_delta)414 void ts_minus(struct timespec *ts_end, struct timespec *ts_start,
415 struct timespec *ts_delta)
416 {
417 if (ts_end == NULL || ts_start == NULL || ts_delta == NULL) {
418 printf("ERROR in %s: one or more of the timespecs is NULL",
419 __FUNCTION__);
420 return;
421 }
422
423 ts_delta->tv_sec = ts_end->tv_sec - ts_start->tv_sec;
424 ts_delta->tv_nsec = ts_end->tv_nsec - ts_start->tv_nsec;
425 ts_normalize(ts_delta);
426 }
427
ts_plus(struct timespec * ts_a,struct timespec * ts_b,struct timespec * ts_sum)428 void ts_plus(struct timespec *ts_a, struct timespec *ts_b,
429 struct timespec *ts_sum)
430 {
431 if (ts_a == NULL || ts_b == NULL || ts_sum == NULL) {
432 printf("ERROR in %s: one or more of the timespecs is NULL",
433 __FUNCTION__);
434 return;
435 }
436
437 ts_sum->tv_sec = ts_a->tv_sec + ts_b->tv_sec;
438 ts_sum->tv_nsec = ts_a->tv_nsec + ts_b->tv_nsec;
439 ts_normalize(ts_sum);
440 }
441
ts_normalize(struct timespec * ts)442 void ts_normalize(struct timespec *ts)
443 {
444 if (ts == NULL) {
445 /* FIXME: write a real error logging system */
446 printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
447 return;
448 }
449
450 /* get the abs(nsec) < NS_PER_SEC */
451 while (ts->tv_nsec > NS_PER_SEC) {
452 ts->tv_sec++;
453 ts->tv_nsec -= NS_PER_SEC;
454 }
455 while (ts->tv_nsec < -NS_PER_SEC) {
456 ts->tv_sec--;
457 ts->tv_nsec += NS_PER_SEC;
458 }
459
460 /* get the values to the same polarity */
461 if (ts->tv_sec > 0 && ts->tv_nsec < 0) {
462 ts->tv_sec--;
463 ts->tv_nsec += NS_PER_SEC;
464 }
465 if (ts->tv_sec < 0 && ts->tv_nsec > 0) {
466 ts->tv_sec++;
467 ts->tv_nsec -= NS_PER_SEC;
468 }
469 }
470
ts_to_nsec(struct timespec * ts,nsec_t * ns)471 int ts_to_nsec(struct timespec *ts, nsec_t * ns)
472 {
473 struct timespec t;
474 if (ts == NULL) {
475 /* FIXME: write a real error logging system */
476 printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
477 return -1;
478 }
479 t.tv_sec = ts->tv_sec;
480 t.tv_nsec = ts->tv_nsec;
481 ts_normalize(&t);
482
483 if (t.tv_sec <= 0 && t.tv_nsec < 0) {
484 printf("ERROR in %s: ts is negative\n", __FUNCTION__);
485 return -1;
486 }
487
488 *ns = (nsec_t) ts->tv_sec * NS_PER_SEC + ts->tv_nsec;
489 return 0;
490 }
491
nsec_to_ts(nsec_t ns,struct timespec * ts)492 void nsec_to_ts(nsec_t ns, struct timespec *ts)
493 {
494 if (ts == NULL) {
495 /* FIXME: write a real error logging system */
496 printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
497 return;
498 }
499 ts->tv_sec = ns / NS_PER_SEC;
500 ts->tv_nsec = ns % NS_PER_SEC;
501 }
502
503 /* return difference in microseconds */
tsc_minus(unsigned long long tsc_start,unsigned long long tsc_end)504 unsigned long long tsc_minus(unsigned long long tsc_start,
505 unsigned long long tsc_end)
506 {
507 unsigned long long delta;
508 if (tsc_start <= tsc_end)
509 delta = tsc_end - tsc_start;
510 else {
511 delta = ULL_MAX - (tsc_end - tsc_start) + 1;
512 printf("TSC wrapped, delta=%llu\n", delta);
513 }
514 return delta;
515 }
516
rt_nanosleep_until(nsec_t ns)517 void rt_nanosleep_until(nsec_t ns)
518 {
519 struct timespec ts_sleep, ts_rem;
520 int rc;
521 nsec_to_ts(ns, &ts_sleep);
522 rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts_sleep,
523 &ts_rem);
524 /* FIXME: when should we display the remainder ? */
525 if (rc != 0) {
526 printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n",
527 (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec);
528 }
529 }
530
rt_nanosleep(nsec_t ns)531 void rt_nanosleep(nsec_t ns)
532 {
533 struct timespec ts_sleep, ts_rem;
534 int rc;
535 nsec_to_ts(ns, &ts_sleep);
536 rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_sleep, &ts_rem);
537 /* FIXME: when should we display the remainder ? */
538 if (rc != 0) {
539 printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n",
540 (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec);
541 }
542 }
543
rt_gettime(void)544 nsec_t rt_gettime(void)
545 {
546 struct timespec ts;
547 nsec_t ns;
548 int rc;
549
550 rc = clock_gettime(CLOCK_MONOTONIC, &ts);
551 if (rc != 0) {
552 printf("ERROR in %s: clock_gettime() returned %d\n",
553 __FUNCTION__, rc);
554 perror("clock_gettime() failed");
555 return 0;
556 }
557
558 ts_to_nsec(&ts, &ns);
559 return ns;
560 }
561
busy_work_ms(int ms)562 void *busy_work_ms(int ms)
563 {
564 busy_work_us(ms * US_PER_MS);
565 return NULL;
566 }
567
busy_work_us(int us)568 void *busy_work_us(int us)
569 {
570 volatile int i;
571 nsec_t start, now;
572 int delta; /* time in us */
573
574 i = us * iters_per_us;
575
576 start = rt_gettime();
577 while (--i > 0) {
578 continue;
579 }
580 now = rt_gettime();
581
582 delta = (now - start) / NS_PER_US;
583 /* uncomment to tune to your machine */
584 /* printf("busy_work_us requested: %dus actual: %dus\n", us, delta); */
585 return NULL;
586 }
587
init_pi_mutex(pthread_mutex_t * m)588 void init_pi_mutex(pthread_mutex_t * m)
589 {
590 #if HAS_PRIORITY_INHERIT
591 pthread_mutexattr_t attr;
592 int ret;
593 int protocol;
594
595 if ((ret = pthread_mutexattr_init(&attr)) != 0) {
596 printf("Failed to init mutexattr: %d (%s)\n", ret,
597 strerror(ret));
598 };
599 if (_use_pi
600 && (ret =
601 pthread_mutexattr_setprotocol(&attr,
602 PTHREAD_PRIO_INHERIT)) != 0) {
603 printf("Can't set protocol prio inherit: %d (%s)\n", ret,
604 strerror(ret));
605 }
606 if ((ret = pthread_mutexattr_getprotocol(&attr, &protocol)) != 0) {
607 printf("Can't get mutexattr protocol: %d (%s)\n", ret,
608 strerror(ret));
609 }
610 if ((ret = pthread_mutex_init(m, &attr)) != 0) {
611 printf("Failed to init mutex: %d (%s)\n", ret, strerror(ret));
612 }
613 #endif
614
615 /* FIXME: does any of this need to be destroyed ? */
616 }
617
618 /* Write the entirety of data. Complain if unable to do so. */
write_or_complain(int fd,const void * data,size_t len)619 static void write_or_complain(int fd, const void *data, size_t len)
620 {
621 const char *remaining = data;
622
623 while (len > 0) {
624 ssize_t ret = write(fd, remaining, len);
625 if (ret <= 0) {
626 if (errno != EAGAIN && errno != EINTR) {
627 perror("write");
628 return;
629 }
630 } else {
631 remaining += ret;
632 len -= ret;
633 }
634 }
635 }
636
637 /* Write the given data to the existing file specified by pathname. Complain
638 * if unable to do so. */
write_file(const char * pathname,const void * data,size_t len)639 static void write_file(const char *pathname, const void *data, size_t len)
640 {
641 int fd = open(pathname, O_WRONLY);
642 if (fd < 0) {
643 printf("Failed to open file \"%s\": %d (%s)\n",
644 pathname, errno, strerror(errno));
645 return;
646 }
647
648 write_or_complain(fd, data, len);
649
650 if (close(fd) < 0) {
651 printf("Failed to close file \"%s\": %d (%s)\n",
652 pathname, errno, strerror(errno));
653 }
654 }
655
656 /* Write the given '\0'-terminated string to the existing file specified by
657 * pathname. Complain if unable to do so. */
write_string_to_file(const char * pathname,const char * string)658 static void write_string_to_file(const char *pathname, const char *string)
659 {
660 write_file(pathname, string, strlen(string));
661 }
662
read_and_print(const char * pathname,int output_fd)663 static void read_and_print(const char *pathname, int output_fd)
664 {
665 char data[4096];
666 int fd = open(pathname, O_RDONLY);
667 if (fd < 0) {
668 printf("Failed to open file \"%s\": %d (%s)\n",
669 pathname, errno, strerror(errno));
670 return;
671 }
672
673 while (1) {
674 ssize_t ret = read(fd, data, sizeof(data));
675 if (ret < 0) {
676 if (errno != EAGAIN && errno != EINTR) {
677 printf
678 ("Failed to read from file \"%s\": %d (%s)\n",
679 pathname, errno, strerror(errno));
680 break;
681 }
682 } else if (ret == 0)
683 break;
684 else
685 write_or_complain(output_fd, data, ret);
686 }
687
688 if (close(fd) < 0) {
689 printf("Failed to close file \"%s\": %d (%s)\n",
690 pathname, errno, strerror(errno));
691 }
692 }
693
latency_trace_enable(void)694 void latency_trace_enable(void)
695 {
696 printf("Enabling latency tracer.\n");
697 write_string_to_file("/proc/sys/kernel/trace_use_raw_cycles", "1");
698 write_string_to_file("/proc/sys/kernel/trace_all_cpus", "1");
699 write_string_to_file("/proc/sys/kernel/trace_enabled", "1");
700 write_string_to_file("/proc/sys/kernel/trace_freerunning", "1");
701 write_string_to_file("/proc/sys/kernel/trace_print_on_crash", "0");
702 write_string_to_file("/proc/sys/kernel/trace_user_triggered", "1");
703 write_string_to_file("/proc/sys/kernel/trace_user_trigger_irq", "-1");
704 write_string_to_file("/proc/sys/kernel/trace_verbose", "0");
705 write_string_to_file("/proc/sys/kernel/preempt_thresh", "0");
706 write_string_to_file("/proc/sys/kernel/wakeup_timing", "0");
707 write_string_to_file("/proc/sys/kernel/mcount_enabled", "1");
708 write_string_to_file("/proc/sys/kernel/preempt_max_latency", "0");
709 }
710
711 #ifndef PR_SET_TRACING
712 #define PR_SET_TRACING 0
713 #endif
714
latency_trace_start(void)715 void latency_trace_start(void)
716 {
717 if (prctl(PR_SET_TRACING, 1) < 0)
718 perror("Failed to start tracing");
719 }
720
latency_trace_stop(void)721 void latency_trace_stop(void)
722 {
723 if (prctl(PR_SET_TRACING, 0) < 0)
724 perror("Failed to stop tracing");
725 }
726
latency_trace_print(void)727 void latency_trace_print(void)
728 {
729 read_and_print("/proc/latency_trace", STDOUT_FILENO);
730 }
731