Home | History | Annotate | Download | only in src
      1 /*
      2  * RT signal roundtrip test software
      3  *
      4  * (C) 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 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 
     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 
     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  */
     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 */
    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 */
    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 
    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 
    286 static void sighand(int sig)
    287 {
    288 	shutdown = 1;
    289 }
    290 
    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 
    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