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
37extern 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 */
59struct 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 */
72struct 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
85static int shutdown;
86static int tracelimit = 0;
87static int ftrace = 0;
88static int oldtrace = 0;
89
90/* Backup of kernel variables that we modify */
91static struct kvars {
92	char name[KVARNAMELEN];
93	int value;
94} kv[KVARS];
95
96static char *procfileprefix = "/proc/sys/kernel/";
97
98static 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
129static 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
154static 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
167static 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
175static 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 */
197void *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
344out:
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 */
367static 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
391static int use_nanosleep;
392static int timermode  = TIMER_ABSTIME;
393static int use_system;
394static int priority;
395static int num_threads = 1;
396static int max_cycles;
397static int clocksel = 0;
398static int verbose;
399static int quiet;
400static int interval = 1000;
401static int distance = 500;
402
403static int clocksources[] = {
404	CLOCK_MONOTONIC,
405	CLOCK_REALTIME,
406};
407
408/* Process commandline options */
409static 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
467static 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
482static 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
492static void sighand(int sig)
493{
494	shutdown = 1;
495}
496
497static 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
519int 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