1 /*
2  * RT signal roundtrip test software
3  *
4  * (C) 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 Veriosn
8  * 2 as published by the Free Software Foundation;
9  *
10  */
11 
12 #define VERSION_STRING "V 0.3"
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 
36 #define USEC_PER_SEC		1000000
37 #define NSEC_PER_SEC		1000000000
38 
39 /* Must be power of 2 ! */
40 #define VALBUF_SIZE		16384
41 
42 /* Struct to transfer parameters to the thread */
43 struct thread_param {
44 	int id;
45 	int prio;
46 	int signal;
47 	unsigned long max_cycles;
48 	struct thread_stat *stats;
49 	int bufmsk;
50 };
51 
52 /* Struct for statistics */
53 struct thread_stat {
54 	unsigned long cycles;
55 	unsigned long cyclesread;
56 	long min;
57 	long max;
58 	long act;
59 	double avg;
60 	long *values;
61 	pthread_t thread;
62 	pthread_t tothread;
63 	int threadstarted;
64 	int tid;
65 };
66 
67 static int shutdown;
68 static int tracelimit = 0;
69 static int ftrace = 0;
70 static int oldtrace = 0;
71 
tsnorm(struct timespec * ts)72 static inline void tsnorm(struct timespec *ts)
73 {
74 	while (ts->tv_nsec >= NSEC_PER_SEC) {
75 		ts->tv_nsec -= NSEC_PER_SEC;
76 		ts->tv_sec++;
77 	}
78 }
79 
calcdiff(struct timespec t1,struct timespec t2)80 static inline long calcdiff(struct timespec t1, struct timespec t2)
81 {
82 	long diff;
83 	diff = USEC_PER_SEC * ((int) t1.tv_sec - (int) t2.tv_sec);
84 	diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000;
85 	return diff;
86 }
87 
88 /*
89  * signal thread
90  *
91  */
signalthread(void * param)92 void *signalthread(void *param)
93 {
94 	struct thread_param *par = param;
95 	struct sched_param schedp;
96 	sigset_t sigset;
97 	struct timespec before, after;
98 	struct thread_stat *stat = par->stats;
99 	int policy = par->prio ? SCHED_FIFO : SCHED_OTHER;
100 	int stopped = 0;
101 	int first = 1;
102 
103 	if (tracelimit) {
104 		system("echo 1 > /proc/sys/kernel/trace_all_cpus");
105 		system("echo 1 > /proc/sys/kernel/trace_freerunning");
106 		system("echo 0 > /proc/sys/kernel/trace_print_at_crash");
107 		system("echo 1 > /proc/sys/kernel/trace_user_triggered");
108 		system("echo -1 > /proc/sys/kernel/trace_user_trigger_irq");
109 		system("echo 0 > /proc/sys/kernel/trace_verbose");
110 		system("echo 0 > /proc/sys/kernel/preempt_thresh");
111 		system("echo 0 > /proc/sys/kernel/wakeup_timing");
112 		system("echo 0 > /proc/sys/kernel/preempt_max_latency");
113 		if (ftrace)
114 			system("echo 1 > /proc/sys/kernel/mcount_enabled");
115 
116 		system("echo 1 > /proc/sys/kernel/trace_enabled");
117 	}
118 
119 	stat->tid = gettid();
120 
121 	sigemptyset(&sigset);
122 	sigaddset(&sigset, par->signal);
123 	sigprocmask(SIG_BLOCK, &sigset, NULL);
124 
125 	memset(&schedp, 0, sizeof(schedp));
126 	schedp.sched_priority = par->prio;
127 	sched_setscheduler(0, policy, &schedp);
128 
129 	stat->threadstarted++;
130 
131 	if (tracelimit) {
132 		if (oldtrace)
133 			gettimeofday(0,(struct timezone *)1);
134 		else
135 			prctl(0, 1);
136 	}
137 
138 	clock_gettime(CLOCK_MONOTONIC, &before);
139 
140 	while (!shutdown) {
141 		struct timespec now;
142 		long diff;
143 		int sigs;
144 
145 		if (sigwait(&sigset, &sigs) < 0)
146 			goto out;
147 
148 		clock_gettime(CLOCK_MONOTONIC, &after);
149 
150 		/*
151 		 * If it is the first thread, sleep after every 16
152 		 * round trips.
153 		 */
154 		if (!par->id && !(stat->cycles & 0x0F))
155 			usleep(10000);
156 
157 		/* Get current time */
158 		clock_gettime(CLOCK_MONOTONIC, &now);
159 		pthread_kill(stat->tothread, SIGUSR1);
160 
161 		/* Skip the first cycle */
162 		if (first) {
163 			first = 0;
164 			before = now;
165 			continue;
166 		}
167 
168 		diff = calcdiff(after, before);
169 		before = now;
170 		if (diff < stat->min)
171 			stat->min = diff;
172 		if (diff > stat->max)
173 			stat->max = diff;
174 		stat->avg += (double) diff;
175 
176 		if (!stopped && tracelimit && (diff > tracelimit)) {
177 			stopped++;
178 			if (oldtrace)
179 				gettimeofday(0,0);
180 			else
181 				prctl(0, 0);
182 			shutdown++;
183 		}
184 		stat->act = diff;
185 		stat->cycles++;
186 
187 		if (par->bufmsk)
188 			stat->values[stat->cycles & par->bufmsk] = diff;
189 
190 		if (par->max_cycles && par->max_cycles == stat->cycles)
191 			break;
192 	}
193 
194 out:
195 	/* switch to normal */
196 	schedp.sched_priority = 0;
197 	sched_setscheduler(0, SCHED_OTHER, &schedp);
198 
199 	stat->threadstarted = -1;
200 
201 	return NULL;
202 }
203 
204 
205 /* Print usage information */
display_help(void)206 static void display_help(void)
207 {
208 	printf("signaltest %s\n", VERSION_STRING);
209 	printf("Usage:\n"
210 	       "signaltest <options>\n\n"
211 	       "-b USEC  --breaktrace=USEC send break trace command when latency > USEC\n"
212 	       "-f                         function trace (when -b is active)\n"
213 	       "-l LOOPS --loops=LOOPS     number of loops: default=0(endless)\n"
214 	       "-p PRIO  --prio=PRIO       priority of highest prio thread\n"
215 	       "-q       --quiet           print only a summary on exit\n"
216 	       "-t NUM   --threads=NUM     number of threads: default=2\n"
217 	       "-v       --verbose         output values on stdout for statistics\n"
218 	       "                           format: n:c:v n=tasknum c=count v=value in us\n");
219 	exit(0);
220 }
221 
222 static int priority;
223 static int num_threads = 2;
224 static int max_cycles;
225 static int verbose;
226 static int quiet;
227 
228 /* Process commandline options */
process_options(int argc,char * argv[])229 static void process_options (int argc, char *argv[])
230 {
231 	int error = 0;
232 	for (;;) {
233 		int option_index = 0;
234 		/** Options for getopt */
235 		static struct option long_options[] = {
236 			{"breaktrace", required_argument, NULL, 'b'},
237 			{"ftrace", no_argument, NULL, 'f'},
238 			{"loops", required_argument, NULL, 'l'},
239 			{"priority", required_argument, NULL, 'p'},
240 			{"quiet", no_argument, NULL, 'q'},
241 			{"threads", required_argument, NULL, 't'},
242 			{"verbose", no_argument, NULL, 'v'},
243 			{"help", no_argument, NULL, '?'},
244 			{NULL, 0, NULL, 0}
245 		};
246 		int c = getopt_long (argc, argv, "b:fl:p:qt:v",
247 			long_options, &option_index);
248 		if (c == -1)
249 			break;
250 		switch (c) {
251 		case 'b': tracelimit = atoi(optarg); break;
252 		case 'l': max_cycles = atoi(optarg); break;
253 		case 'p': priority = atoi(optarg); break;
254 		case 'q': quiet = 1; break;
255 		case 't': num_threads = atoi(optarg); break;
256 		case 'v': verbose = 1; break;
257 		case '?': error = 1; break;
258 		}
259 	}
260 
261 	if (priority < 0 || priority > 99)
262 		error = 1;
263 
264 	if (num_threads < 2)
265 		error = 1;
266 
267 	if (error)
268 		display_help ();
269 }
270 
check_kernel(void)271 static void check_kernel(void)
272 {
273 	size_t len;
274 	char ver[256];
275 	int fd, maj, min, sub;
276 
277 	fd = open("/proc/version", O_RDONLY, 0666);
278 	len = read(fd, ver, 255);
279 	close(fd);
280 	ver[len-1] = 0x0;
281 	sscanf(ver, "Linux version %d.%d.%d", &maj, &min, &sub);
282 	if (maj == 2 && min == 6 && sub < 18)
283 		oldtrace = 1;
284 }
285 
sighand(int sig)286 static void sighand(int sig)
287 {
288 	shutdown = 1;
289 }
290 
print_stat(struct thread_param * par,int index,int verbose)291 static void print_stat(struct thread_param *par, int index, int verbose)
292 {
293 	struct thread_stat *stat = par->stats;
294 
295 	if (!verbose) {
296 		if (quiet != 1) {
297 			printf("T:%2d (%5d) P:%2d C:%7lu "
298 			       "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n",
299 			       index, stat->tid, par->prio,
300 			       stat->cycles, stat->min, stat->act,
301 			       stat->cycles ?
302 			       (long)(stat->avg/stat->cycles) : 0, stat->max);
303 		}
304 	} else {
305 		while (stat->cycles != stat->cyclesread) {
306 			long diff = stat->values[stat->cyclesread & par->bufmsk];
307 			printf("%8d:%8lu:%8ld\n", index, stat->cyclesread, diff);
308 			stat->cyclesread++;
309 		}
310 	}
311 }
312 
main(int argc,char ** argv)313 int main(int argc, char **argv)
314 {
315 	sigset_t sigset;
316 	int signum = SIGUSR1;
317 	struct thread_param *par;
318 	struct thread_stat *stat;
319 	int i, ret = -1;
320 
321 	if (geteuid()) {
322 		printf("need to run as root!\n");
323 		exit(-1);
324 	}
325 
326 	process_options(argc, argv);
327 
328 	check_kernel();
329 
330 	sigemptyset(&sigset);
331 	sigaddset(&sigset, signum);
332 	sigprocmask (SIG_BLOCK, &sigset, NULL);
333 
334 	signal(SIGINT, sighand);
335 	signal(SIGTERM, sighand);
336 
337 	par = calloc(num_threads, sizeof(struct thread_param));
338 	if (!par)
339 		goto out;
340 	stat = calloc(num_threads, sizeof(struct thread_stat));
341 	if (!stat)
342 		goto outpar;
343 
344 	for (i = 0; i < num_threads; i++) {
345 		if (verbose) {
346 			stat[i].values = calloc(VALBUF_SIZE, sizeof(long));
347 			if (!stat[i].values)
348 				goto outall;
349 			par[i].bufmsk = VALBUF_SIZE - 1;
350 		}
351 
352 		par[i].id = i;
353 		par[i].prio = priority;
354 #if 0
355 		if (priority)
356 			priority--;
357 #endif
358 		par[i].signal = signum;
359 		par[i].max_cycles = max_cycles;
360 		par[i].stats = &stat[i];
361 		stat[i].min = 1000000;
362 		stat[i].max = -1000000;
363 		stat[i].avg = 0.0;
364 		stat[i].threadstarted = 1;
365 		pthread_create(&stat[i].thread, NULL, signalthread, &par[i]);
366 	}
367 
368 	while (!shutdown) {
369 		int allstarted = 1;
370 
371 		for (i = 0; i < num_threads; i++) {
372 			if (stat[i].threadstarted != 2)
373 				allstarted = 0;
374 		}
375 		if (!allstarted)
376 			continue;
377 
378 		for (i = 0; i < num_threads - 1; i++)
379 			stat[i].tothread = stat[i+1].thread;
380 		stat[i].tothread = stat[0].thread;
381 		break;
382 	}
383 	pthread_kill(stat[0].thread, signum);
384 
385 	while (!shutdown) {
386 		char lavg[256];
387 		int fd, len, allstopped = 0;
388 
389 		if (!verbose && !quiet) {
390 			fd = open("/proc/loadavg", O_RDONLY, 0666);
391 			len = read(fd, &lavg, 255);
392 			close(fd);
393 			lavg[len-1] = 0x0;
394 			printf("%s          \n\n", lavg);
395 		}
396 
397 		print_stat(&par[0], 0, verbose);
398 		if(max_cycles && stat[0].cycles >= max_cycles)
399 			allstopped++;
400 
401 		usleep(10000);
402 		if (shutdown || allstopped)
403 			break;
404 		if (!verbose && !quiet)
405 			printf("\033[%dA", 3);
406 	}
407 	ret = 0;
408  outall:
409 	shutdown = 1;
410 	usleep(50000);
411 	if (quiet)
412 		quiet = 2;
413 	for (i = 0; i < num_threads; i++) {
414 		if (stat[i].threadstarted > 0)
415 			pthread_kill(stat[i].thread, SIGTERM);
416 		if (stat[i].threadstarted) {
417 			pthread_join(stat[i].thread, NULL);
418 			if (quiet)
419 				print_stat(&par[i], i, 0);
420 		}
421 		if (stat[i].values)
422 			free(stat[i].values);
423 	}
424 	free(stat);
425  outpar:
426 	free(par);
427  out:
428 	exit(ret);
429 }
430