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