Home | History | Annotate | Download | only in src
      1 /*
      2  * High resolution timer test software
      3  *
      4  * (C) 2005-2007 Thomas Gleixner <tglx (at) linutronix.de>
      5  *
      6  * This program is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU General Public License Version
      8  * 2 as published by the Free Software Foundation.
      9  *
     10  */
     11 
     12 #define VERSION_STRING "V 0.15"
     13 
     14 #include <fcntl.h>
     15 #include <getopt.h>
     16 #include <pthread.h>
     17 #include <signal.h>
     18 #include <stdlib.h>
     19 #include <stdio.h>
     20 #include <string.h>
     21 #include <time.h>
     22 #include <unistd.h>
     23 
     24 #include <linux/unistd.h>
     25 
     26 #include <sys/prctl.h>
     27 #include <sys/stat.h>
     28 #include <sys/types.h>
     29 #include <sys/time.h>
     30 
     31 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
     32 
     33 /* Ugly, but .... */
     34 #define gettid() syscall(__NR_gettid)
     35 #define sigev_notify_thread_id _sigev_un._tid
     36 
     37 extern int clock_nanosleep(clockid_t __clock_id, int __flags,
     38 			   __const struct timespec *__req,
     39 			   struct timespec *__rem);
     40 
     41 #define USEC_PER_SEC		1000000
     42 #define NSEC_PER_SEC		1000000000
     43 
     44 #define MODE_CYCLIC		0
     45 #define MODE_CLOCK_NANOSLEEP	1
     46 #define MODE_SYS_ITIMER		2
     47 #define MODE_SYS_NANOSLEEP	3
     48 #define MODE_SYS_OFFSET		2
     49 
     50 #define TIMER_RELTIME		0
     51 
     52 /* Must be power of 2 ! */
     53 #define VALBUF_SIZE		16384
     54 
     55 #define KVARS			32
     56 #define KVARNAMELEN		32
     57 
     58 /* Struct to transfer parameters to the thread */
     59 struct thread_param {
     60 	int prio;
     61 	int mode;
     62 	int timermode;
     63 	int signal;
     64 	int clock;
     65 	unsigned long max_cycles;
     66 	struct thread_stat *stats;
     67 	int bufmsk;
     68 	unsigned long interval;
     69 };
     70 
     71 /* Struct for statistics */
     72 struct thread_stat {
     73 	unsigned long cycles;
     74 	unsigned long cyclesread;
     75 	long min;
     76 	long max;
     77 	long act;
     78 	double avg;
     79 	long *values;
     80 	pthread_t thread;
     81 	int threadstarted;
     82 	int tid;
     83 };
     84 
     85 static int shutdown;
     86 static int tracelimit = 0;
     87 static int ftrace = 0;
     88 static int oldtrace = 0;
     89 
     90 /* Backup of kernel variables that we modify */
     91 static struct kvars {
     92 	char name[KVARNAMELEN];
     93 	int value;
     94 } kv[KVARS];
     95 
     96 static char *procfileprefix = "/proc/sys/kernel/";
     97 
     98 static int kernvar(int mode, char *name, int *value)
     99 {
    100 	int retval = 1;
    101 	int procfilepath;
    102 	char procfilename[128];
    103 
    104 	strncpy(procfilename, procfileprefix, sizeof(procfilename));
    105 	strncat(procfilename, name,
    106 		sizeof(procfilename) - sizeof(procfileprefix));
    107 	procfilepath = open(procfilename, mode);
    108 	if (procfilepath >= 0) {
    109 		char buffer[32];
    110 
    111 		if (mode == O_RDONLY) {
    112 			if (read(procfilepath, buffer, sizeof(buffer)) > 0) {
    113 				char *endptr;
    114 				*value = strtol(buffer, &endptr, 0);
    115 				if (endptr != buffer)
    116 					retval = 0;
    117 			}
    118 		} else if (mode == O_WRONLY) {
    119 			snprintf(buffer, sizeof(buffer), "%d\n", *value);
    120 			if (write(procfilepath, buffer, strlen(buffer))
    121 			    == strlen(buffer))
    122 				retval = 0;
    123 		}
    124 		close(procfilepath);
    125 	}
    126 	return retval;
    127 }
    128 
    129 static void setkernvar(char *name, int value)
    130 {
    131 	int i;
    132 	int oldvalue;
    133 
    134 	if (kernvar(O_RDONLY, name, &oldvalue))
    135 		fprintf(stderr, "could not retrieve %s\n", name);
    136 	else {
    137 		for (i = 0; i < KVARS; i++) {
    138 			if (!strcmp(kv[i].name, name))
    139 				break;
    140 			if (kv[i].name[0] == '\0') {
    141 				strncpy(kv[i].name, name, sizeof(kv[i].name));
    142 				kv[i].value = oldvalue;
    143 				break;
    144 			}
    145 		}
    146 		if (i == KVARS)
    147 			fprintf(stderr, "could not backup %s (%d)\n", name,
    148 				oldvalue);
    149 	}
    150 	if (kernvar(O_WRONLY, name, &value))
    151 		fprintf(stderr, "could not set %s to %d\n", name, value);
    152 }
    153 
    154 static void restorekernvars(void)
    155 {
    156 	int i;
    157 
    158 	for (i = 0; i < KVARS; i++) {
    159 		if (kv[i].name[0] != '\0') {
    160 			if (kernvar(O_WRONLY, kv[i].name, &kv[i].value))
    161 				fprintf(stderr, "could not restore %s to %d\n",
    162 					kv[i].name, kv[i].value);
    163 		}
    164 	}
    165 }
    166 
    167 static inline void tsnorm(struct timespec *ts)
    168 {
    169 	while (ts->tv_nsec >= NSEC_PER_SEC) {
    170 		ts->tv_nsec -= NSEC_PER_SEC;
    171 		ts->tv_sec++;
    172 	}
    173 }
    174 
    175 static inline long calcdiff(struct timespec t1, struct timespec t2)
    176 {
    177 	long diff;
    178 	diff = USEC_PER_SEC * ((int) t1.tv_sec - (int) t2.tv_sec);
    179 	diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000;
    180 	return diff;
    181 }
    182 
    183 /*
    184  * timer thread
    185  *
    186  * Modes:
    187  * - clock_nanosleep based
    188  * - cyclic timer based
    189  *
    190  * Clock:
    191  * - CLOCK_MONOTONIC
    192  * - CLOCK_REALTIME
    193  * - CLOCK_MONOTONIC_HR
    194  * - CLOCK_REALTIME_HR
    195  *
    196  */
    197 void *timerthread(void *param)
    198 {
    199 	struct thread_param *par = param;
    200 	struct sched_param schedp;
    201 	struct sigevent sigev;
    202 	sigset_t sigset;
    203 	timer_t timer;
    204 	struct timespec now, next, interval;
    205 	struct itimerval itimer;
    206 	struct itimerspec tspec;
    207 	struct thread_stat *stat = par->stats;
    208 	int policy = par->prio ? SCHED_FIFO : SCHED_OTHER;
    209 	int stopped = 0;
    210 
    211 	interval.tv_sec = par->interval / USEC_PER_SEC;
    212 	interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000;
    213 
    214 	if (tracelimit) {
    215 		setkernvar("trace_all_cpus", 1);
    216 		setkernvar("trace_freerunning", 1);
    217 		setkernvar("trace_print_on_crash", 0);
    218 		setkernvar("trace_user_triggered", 1);
    219 		setkernvar("trace_user_trigger_irq", -1);
    220 		setkernvar("trace_verbose", 0);
    221 		setkernvar("preempt_thresh", 0);
    222 		setkernvar("wakeup_timing", 0);
    223 		setkernvar("preempt_max_latency", 0);
    224 		if (ftrace)
    225 			setkernvar("mcount_enabled", 1);
    226 		setkernvar("trace_enabled", 1);
    227 	}
    228 
    229 	stat->tid = gettid();
    230 
    231 	sigemptyset(&sigset);
    232 	sigaddset(&sigset, par->signal);
    233 	sigprocmask(SIG_BLOCK, &sigset, NULL);
    234 
    235 	if (par->mode == MODE_CYCLIC) {
    236 		sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL;
    237 		sigev.sigev_signo = par->signal;
    238 		sigev.sigev_notify_thread_id = stat->tid;
    239 		timer_create(par->clock, &sigev, &timer);
    240 		tspec.it_interval = interval;
    241 	}
    242 
    243 	memset(&schedp, 0, sizeof(schedp));
    244 	schedp.sched_priority = par->prio;
    245 	sched_setscheduler(0, policy, &schedp);
    246 
    247 	/* Get current time */
    248 	clock_gettime(par->clock, &now);
    249 	next = now;
    250 	next.tv_sec++;
    251 
    252 	if (par->mode == MODE_CYCLIC) {
    253 		if (par->timermode == TIMER_ABSTIME)
    254 			tspec.it_value = next;
    255 		else {
    256 			tspec.it_value.tv_nsec = 0;
    257 			tspec.it_value.tv_sec = 1;
    258 		}
    259 		timer_settime(timer, par->timermode, &tspec, NULL);
    260 	}
    261 
    262 	if (par->mode == MODE_SYS_ITIMER) {
    263 		itimer.it_value.tv_sec = 1;
    264 		itimer.it_value.tv_usec = 0;
    265 		itimer.it_interval.tv_sec = interval.tv_sec;
    266 		itimer.it_interval.tv_usec = interval.tv_nsec / 1000;
    267 		setitimer (ITIMER_REAL,  &itimer, NULL);
    268 	}
    269 
    270 	stat->threadstarted++;
    271 
    272 	if (tracelimit) {
    273 		if (oldtrace)
    274 			gettimeofday(0,(struct timezone *)1);
    275 		else
    276 			prctl(0, 1);
    277 	}
    278 	while (!shutdown) {
    279 
    280 		long diff;
    281 		int sigs;
    282 
    283 		/* Wait for next period */
    284 		switch (par->mode) {
    285 		case MODE_CYCLIC:
    286 		case MODE_SYS_ITIMER:
    287 			if (sigwait(&sigset, &sigs) < 0)
    288 				goto out;
    289 			break;
    290 
    291 		case MODE_CLOCK_NANOSLEEP:
    292 			if (par->timermode == TIMER_ABSTIME)
    293 				clock_nanosleep(par->clock, TIMER_ABSTIME,
    294 						&next, NULL);
    295 			else {
    296 				clock_gettime(par->clock, &now);
    297 				clock_nanosleep(par->clock, TIMER_RELTIME,
    298 						&interval, NULL);
    299 				next.tv_sec = now.tv_sec + interval.tv_sec;
    300 				next.tv_nsec = now.tv_nsec + interval.tv_nsec;
    301 				tsnorm(&next);
    302 			}
    303 			break;
    304 
    305 		case MODE_SYS_NANOSLEEP:
    306 			clock_gettime(par->clock, &now);
    307 			nanosleep(&interval, NULL);
    308 			next.tv_sec = now.tv_sec + interval.tv_sec;
    309 			next.tv_nsec = now.tv_nsec + interval.tv_nsec;
    310 			tsnorm(&next);
    311 			break;
    312 		}
    313 		clock_gettime(par->clock, &now);
    314 
    315 		diff = calcdiff(now, next);
    316 		if (diff < stat->min)
    317 			stat->min = diff;
    318 		if (diff > stat->max)
    319 			stat->max = diff;
    320 		stat->avg += (double) diff;
    321 
    322 		if (!stopped && tracelimit && (diff > tracelimit)) {
    323 			stopped++;
    324 			if (oldtrace)
    325 				gettimeofday(0,0);
    326 			else
    327 				prctl(0, 0);
    328 			shutdown++;
    329 		}
    330 		stat->act = diff;
    331 		stat->cycles++;
    332 
    333 		if (par->bufmsk)
    334 			stat->values[stat->cycles & par->bufmsk] = diff;
    335 
    336 		next.tv_sec += interval.tv_sec;
    337 		next.tv_nsec += interval.tv_nsec;
    338 		tsnorm(&next);
    339 
    340 		if (par->max_cycles && par->max_cycles == stat->cycles)
    341 			break;
    342 	}
    343 
    344 out:
    345 	if (par->mode == MODE_CYCLIC)
    346 		timer_delete(timer);
    347 
    348 	if (par->mode == MODE_SYS_ITIMER) {
    349 		itimer.it_value.tv_sec = 0;
    350 		itimer.it_value.tv_usec = 0;
    351 		itimer.it_interval.tv_sec = 0;
    352 		itimer.it_interval.tv_usec = 0;
    353 		setitimer (ITIMER_REAL,  &itimer, NULL);
    354 	}
    355 
    356 	/* switch to normal */
    357 	schedp.sched_priority = 0;
    358 	sched_setscheduler(0, SCHED_OTHER, &schedp);
    359 
    360 	stat->threadstarted = -1;
    361 
    362 	return NULL;
    363 }
    364 
    365 
    366 /* Print usage information */
    367 static void display_help(void)
    368 {
    369 	printf("cyclictest %s\n", VERSION_STRING);
    370 	printf("Usage:\n"
    371 	       "cyclictest <options>\n\n"
    372 	       "-b USEC  --breaktrace=USEC send break trace command when latency > USEC\n"
    373 	       "-c CLOCK --clock=CLOCK     select clock\n"
    374 	       "                           0 = CLOCK_MONOTONIC (default)\n"
    375 	       "                           1 = CLOCK_REALTIME\n"
    376 	       "-d DIST  --distance=DIST   distance of thread intervals in us default=500\n"
    377 	       "-f                         function trace (when -b is active)\n"
    378 	       "-i INTV  --interval=INTV   base interval of thread in us default=1000\n"
    379 	       "-l LOOPS --loops=LOOPS     number of loops: default=0(endless)\n"
    380 	       "-n       --nanosleep       use clock_nanosleep\n"
    381 	       "-p PRIO  --prio=PRIO       priority of highest prio thread\n"
    382 	       "-q       --quiet           print only a summary on exit\n"
    383 	       "-r       --relative        use relative timer instead of absolute\n"
    384 	       "-s       --system          use sys_nanosleep and sys_setitimer\n"
    385 	       "-t NUM   --threads=NUM     number of threads: default=1\n"
    386 	       "-v       --verbose         output values on stdout for statistics\n"
    387 	       "                           format: n:c:v n=tasknum c=count v=value in us\n");
    388 	exit(0);
    389 }
    390 
    391 static int use_nanosleep;
    392 static int timermode  = TIMER_ABSTIME;
    393 static int use_system;
    394 static int priority;
    395 static int num_threads = 1;
    396 static int max_cycles;
    397 static int clocksel = 0;
    398 static int verbose;
    399 static int quiet;
    400 static int interval = 1000;
    401 static int distance = 500;
    402 
    403 static int clocksources[] = {
    404 	CLOCK_MONOTONIC,
    405 	CLOCK_REALTIME,
    406 };
    407 
    408 /* Process commandline options */
    409 static void process_options (int argc, char *argv[])
    410 {
    411 	int error = 0;
    412 	for (;;) {
    413 		int option_index = 0;
    414 		/** Options for getopt */
    415 		static struct option long_options[] = {
    416 			{"breaktrace", required_argument, NULL, 'b'},
    417 			{"clock", required_argument, NULL, 'c'},
    418 			{"distance", required_argument, NULL, 'd'},
    419 			{"ftrace", no_argument, NULL, 'f'},
    420 			{"interval", required_argument, NULL, 'i'},
    421 			{"loops", required_argument, NULL, 'l'},
    422 			{"nanosleep", no_argument, NULL, 'n'},
    423 			{"priority", required_argument, NULL, 'p'},
    424 			{"quiet", no_argument, NULL, 'q'},
    425 			{"relative", no_argument, NULL, 'r'},
    426 			{"system", no_argument, NULL, 's'},
    427 			{"threads", required_argument, NULL, 't'},
    428 			{"verbose", no_argument, NULL, 'v'},
    429 			{"help", no_argument, NULL, '?'},
    430 			{NULL, 0, NULL, 0}
    431 		};
    432 		int c = getopt_long (argc, argv, "b:c:d:fi:l:np:qrst:v",
    433 			long_options, &option_index);
    434 		if (c == -1)
    435 			break;
    436 		switch (c) {
    437 		case 'b': tracelimit = atoi(optarg); break;
    438 		case 'c': clocksel = atoi(optarg); break;
    439 		case 'd': distance = atoi(optarg); break;
    440 		case 'f': ftrace = 1; break;
    441 		case 'i': interval = atoi(optarg); break;
    442 		case 'l': max_cycles = atoi(optarg); break;
    443 		case 'n': use_nanosleep = MODE_CLOCK_NANOSLEEP; break;
    444 		case 'p': priority = atoi(optarg); break;
    445 		case 'q': quiet = 1; break;
    446 		case 'r': timermode = TIMER_RELTIME; break;
    447 		case 's': use_system = MODE_SYS_OFFSET; break;
    448 		case 't': num_threads = atoi(optarg); break;
    449 		case 'v': verbose = 1; break;
    450 		case '?': error = 1; break;
    451 		}
    452 	}
    453 
    454 	if (clocksel < 0 || clocksel > ARRAY_SIZE(clocksources))
    455 		error = 1;
    456 
    457 	if (priority < 0 || priority > 99)
    458 		error = 1;
    459 
    460 	if (num_threads < 1)
    461 		error = 1;
    462 
    463 	if (error)
    464 		display_help ();
    465 }
    466 
    467 static void check_kernel(void)
    468 {
    469 	size_t len;
    470 	char ver[256];
    471 	int fd, maj, min, sub;
    472 
    473 	fd = open("/proc/version", O_RDONLY, 0666);
    474 	len = read(fd, ver, 255);
    475 	close(fd);
    476 	ver[len-1] = 0x0;
    477 	sscanf(ver, "Linux version %d.%d.%d", &maj, &min, &sub);
    478 	if (maj == 2 && min == 6 && sub < 18)
    479 		oldtrace = 1;
    480 }
    481 
    482 static int check_timer(void)
    483 {
    484 	struct timespec ts;
    485 
    486 	if (clock_getres(CLOCK_MONOTONIC, &ts))
    487 		return 1;
    488 
    489 	return (ts.tv_sec != 0 || ts.tv_nsec != 1);
    490 }
    491 
    492 static void sighand(int sig)
    493 {
    494 	shutdown = 1;
    495 }
    496 
    497 static void print_stat(struct thread_param *par, int index, int verbose)
    498 {
    499 	struct thread_stat *stat = par->stats;
    500 
    501 	if (!verbose) {
    502 		if (quiet != 1) {
    503 			printf("T:%2d (%5d) P:%2d I:%ld C:%7lu "
    504 			       "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n",
    505 			       index, stat->tid, par->prio, par->interval,
    506 			       stat->cycles, stat->min, stat->act,
    507 			       stat->cycles ?
    508 			       (long)(stat->avg/stat->cycles) : 0, stat->max);
    509 		}
    510 	} else {
    511 		while (stat->cycles != stat->cyclesread) {
    512 			long diff = stat->values[stat->cyclesread & par->bufmsk];
    513 			printf("%8d:%8lu:%8ld\n", index, stat->cyclesread, diff);
    514 			stat->cyclesread++;
    515 		}
    516 	}
    517 }
    518 
    519 int main(int argc, char **argv)
    520 {
    521 	sigset_t sigset;
    522 	int signum = SIGALRM;
    523 	int mode;
    524 	struct thread_param *par;
    525 	struct thread_stat *stat;
    526 	int i, ret = -1;
    527 
    528 	if (geteuid()) {
    529 		fprintf(stderr, "cyclictest: need to run as root!\n");
    530 		exit(-1);
    531 	}
    532 
    533 	process_options(argc, argv);
    534 
    535 	check_kernel();
    536 
    537 	if (check_timer())
    538 		fprintf(stderr, "WARNING: High resolution timers not available\n");
    539 
    540 	mode = use_nanosleep + use_system;
    541 
    542 	sigemptyset(&sigset);
    543 	sigaddset(&sigset, signum);
    544 	sigprocmask (SIG_BLOCK, &sigset, NULL);
    545 
    546 	signal(SIGINT, sighand);
    547 	signal(SIGTERM, sighand);
    548 
    549 	par = calloc(num_threads, sizeof(struct thread_param));
    550 	if (!par)
    551 		goto out;
    552 	stat = calloc(num_threads, sizeof(struct thread_stat));
    553 	if (!stat)
    554 		goto outpar;
    555 
    556 	for (i = 0; i < num_threads; i++) {
    557 		if (verbose) {
    558 			stat[i].values = calloc(VALBUF_SIZE, sizeof(long));
    559 			if (!stat[i].values)
    560 				goto outall;
    561 			par[i].bufmsk = VALBUF_SIZE - 1;
    562 		}
    563 
    564 		par[i].prio = priority;
    565 		if (priority)
    566 			priority--;
    567 		par[i].clock = clocksources[clocksel];
    568 		par[i].mode = mode;
    569 		par[i].timermode = timermode;
    570 		par[i].signal = signum;
    571 		par[i].interval = interval;
    572 		interval += distance;
    573 		par[i].max_cycles = max_cycles;
    574 		par[i].stats = &stat[i];
    575 		stat[i].min = 1000000;
    576 		stat[i].max = -1000000;
    577 		stat[i].avg = 0.0;
    578 		pthread_create(&stat[i].thread, NULL, timerthread, &par[i]);
    579 		stat[i].threadstarted = 1;
    580 	}
    581 
    582 	while (!shutdown) {
    583 		char lavg[256];
    584 		int fd, len, allstopped = 0;
    585 
    586 		if (!verbose && !quiet) {
    587 			fd = open("/proc/loadavg", O_RDONLY, 0666);
    588 			len = read(fd, &lavg, 255);
    589 			close(fd);
    590 			lavg[len-1] = 0x0;
    591 			printf("%s          \n\n", lavg);
    592 		}
    593 
    594 		for (i = 0; i < num_threads; i++) {
    595 
    596 			print_stat(&par[i], i, verbose);
    597 			if(max_cycles && stat[i].cycles >= max_cycles)
    598 				allstopped++;
    599 		}
    600 		usleep(10000);
    601 		if (shutdown || allstopped)
    602 			break;
    603 		if (!verbose && !quiet)
    604 			printf("\033[%dA", num_threads + 2);
    605 	}
    606 	ret = 0;
    607  outall:
    608 	shutdown = 1;
    609 	usleep(50000);
    610 	if (quiet)
    611 		quiet = 2;
    612 	for (i = 0; i < num_threads; i++) {
    613 		if (stat[i].threadstarted > 0)
    614 			pthread_kill(stat[i].thread, SIGTERM);
    615 		if (stat[i].threadstarted) {
    616 			pthread_join(stat[i].thread, NULL);
    617 			if (quiet)
    618 				print_stat(&par[i], i, 0);
    619 		}
    620 		if (stat[i].values)
    621 			free(stat[i].values);
    622 	}
    623 	free(stat);
    624  outpar:
    625 	free(par);
    626  out:
    627 	/* Be a nice program, cleanup */
    628 	restorekernvars();
    629 
    630 	exit(ret);
    631 }
    632