1 /*
2  * High resolution timer test software
3  *
4  * (C) 2005-2007 Thomas Gleixner <tglx@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 
kernvar(int mode,char * name,int * value)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 
setkernvar(char * name,int value)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 
restorekernvars(void)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 
tsnorm(struct timespec * ts)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 
calcdiff(struct timespec t1,struct timespec t2)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  */
timerthread(void * param)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 */
display_help(void)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 */
process_options(int argc,char * argv[])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 
check_kernel(void)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 
check_timer(void)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 
sighand(int sig)492 static void sighand(int sig)
493 {
494 	shutdown = 1;
495 }
496 
print_stat(struct thread_param * par,int index,int verbose)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 
main(int argc,char ** argv)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