| /* |
| * RT signal roundtrip test software |
| * |
| * (C) 2007 Thomas Gleixner <[email protected]> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License Veriosn |
| * 2 as published by the Free Software Foundation; |
| * |
| */ |
| |
| #define VERSION_STRING "V 0.3" |
| |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <linux/unistd.h> |
| |
| #include <sys/prctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
| |
| /* Ugly, but .... */ |
| #define gettid() syscall(__NR_gettid) |
| |
| #define USEC_PER_SEC 1000000 |
| #define NSEC_PER_SEC 1000000000 |
| |
| /* Must be power of 2 ! */ |
| #define VALBUF_SIZE 16384 |
| |
| /* Struct to transfer parameters to the thread */ |
| struct thread_param { |
| int id; |
| int prio; |
| int signal; |
| unsigned long max_cycles; |
| struct thread_stat *stats; |
| int bufmsk; |
| }; |
| |
| /* Struct for statistics */ |
| struct thread_stat { |
| unsigned long cycles; |
| unsigned long cyclesread; |
| long min; |
| long max; |
| long act; |
| double avg; |
| long *values; |
| pthread_t thread; |
| pthread_t tothread; |
| int threadstarted; |
| int tid; |
| }; |
| |
| static int shutdown; |
| static int tracelimit = 0; |
| static int ftrace = 0; |
| static int oldtrace = 0; |
| |
| static inline void tsnorm(struct timespec *ts) |
| { |
| while (ts->tv_nsec >= NSEC_PER_SEC) { |
| ts->tv_nsec -= NSEC_PER_SEC; |
| ts->tv_sec++; |
| } |
| } |
| |
| static inline long calcdiff(struct timespec t1, struct timespec t2) |
| { |
| long diff; |
| diff = USEC_PER_SEC * ((int) t1.tv_sec - (int) t2.tv_sec); |
| diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000; |
| return diff; |
| } |
| |
| /* |
| * signal thread |
| * |
| */ |
| void *signalthread(void *param) |
| { |
| struct thread_param *par = param; |
| struct sched_param schedp; |
| sigset_t sigset; |
| struct timespec before, after; |
| struct thread_stat *stat = par->stats; |
| int policy = par->prio ? SCHED_FIFO : SCHED_OTHER; |
| int stopped = 0; |
| int first = 1; |
| |
| if (tracelimit) { |
| system("echo 1 > /proc/sys/kernel/trace_all_cpus"); |
| system("echo 1 > /proc/sys/kernel/trace_freerunning"); |
| system("echo 0 > /proc/sys/kernel/trace_print_at_crash"); |
| system("echo 1 > /proc/sys/kernel/trace_user_triggered"); |
| system("echo -1 > /proc/sys/kernel/trace_user_trigger_irq"); |
| system("echo 0 > /proc/sys/kernel/trace_verbose"); |
| system("echo 0 > /proc/sys/kernel/preempt_thresh"); |
| system("echo 0 > /proc/sys/kernel/wakeup_timing"); |
| system("echo 0 > /proc/sys/kernel/preempt_max_latency"); |
| if (ftrace) |
| system("echo 1 > /proc/sys/kernel/mcount_enabled"); |
| |
| system("echo 1 > /proc/sys/kernel/trace_enabled"); |
| } |
| |
| stat->tid = gettid(); |
| |
| sigemptyset(&sigset); |
| sigaddset(&sigset, par->signal); |
| sigprocmask(SIG_BLOCK, &sigset, NULL); |
| |
| memset(&schedp, 0, sizeof(schedp)); |
| schedp.sched_priority = par->prio; |
| sched_setscheduler(0, policy, &schedp); |
| |
| stat->threadstarted++; |
| |
| if (tracelimit) { |
| if (oldtrace) |
| gettimeofday(0,(struct timezone *)1); |
| else |
| prctl(0, 1); |
| } |
| |
| clock_gettime(CLOCK_MONOTONIC, &before); |
| |
| while (!shutdown) { |
| struct timespec now; |
| long diff; |
| int sigs; |
| |
| if (sigwait(&sigset, &sigs) < 0) |
| goto out; |
| |
| clock_gettime(CLOCK_MONOTONIC, &after); |
| |
| /* |
| * If it is the first thread, sleep after every 16 |
| * round trips. |
| */ |
| if (!par->id && !(stat->cycles & 0x0F)) |
| usleep(10000); |
| |
| /* Get current time */ |
| clock_gettime(CLOCK_MONOTONIC, &now); |
| pthread_kill(stat->tothread, SIGUSR1); |
| |
| /* Skip the first cycle */ |
| if (first) { |
| first = 0; |
| before = now; |
| continue; |
| } |
| |
| diff = calcdiff(after, before); |
| before = now; |
| if (diff < stat->min) |
| stat->min = diff; |
| if (diff > stat->max) |
| stat->max = diff; |
| stat->avg += (double) diff; |
| |
| if (!stopped && tracelimit && (diff > tracelimit)) { |
| stopped++; |
| if (oldtrace) |
| gettimeofday(0,0); |
| else |
| prctl(0, 0); |
| shutdown++; |
| } |
| stat->act = diff; |
| stat->cycles++; |
| |
| if (par->bufmsk) |
| stat->values[stat->cycles & par->bufmsk] = diff; |
| |
| if (par->max_cycles && par->max_cycles == stat->cycles) |
| break; |
| } |
| |
| out: |
| /* switch to normal */ |
| schedp.sched_priority = 0; |
| sched_setscheduler(0, SCHED_OTHER, &schedp); |
| |
| stat->threadstarted = -1; |
| |
| return NULL; |
| } |
| |
| |
| /* Print usage information */ |
| static void display_help(void) |
| { |
| printf("signaltest %s\n", VERSION_STRING); |
| printf("Usage:\n" |
| "signaltest <options>\n\n" |
| "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" |
| "-f function trace (when -b is active)\n" |
| "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" |
| "-p PRIO --prio=PRIO priority of highest prio thread\n" |
| "-q --quiet print only a summary on exit\n" |
| "-t NUM --threads=NUM number of threads: default=2\n" |
| "-v --verbose output values on stdout for statistics\n" |
| " format: n:c:v n=tasknum c=count v=value in us\n"); |
| exit(0); |
| } |
| |
| static int priority; |
| static int num_threads = 2; |
| static int max_cycles; |
| static int verbose; |
| static int quiet; |
| |
| /* Process commandline options */ |
| static void process_options (int argc, char *argv[]) |
| { |
| int error = 0; |
| for (;;) { |
| int option_index = 0; |
| /** Options for getopt */ |
| static struct option long_options[] = { |
| {"breaktrace", required_argument, NULL, 'b'}, |
| {"ftrace", no_argument, NULL, 'f'}, |
| {"loops", required_argument, NULL, 'l'}, |
| {"priority", required_argument, NULL, 'p'}, |
| {"quiet", no_argument, NULL, 'q'}, |
| {"threads", required_argument, NULL, 't'}, |
| {"verbose", no_argument, NULL, 'v'}, |
| {"help", no_argument, NULL, '?'}, |
| {NULL, 0, NULL, 0} |
| }; |
| int c = getopt_long (argc, argv, "b:fl:p:qt:v", |
| long_options, &option_index); |
| if (c == -1) |
| break; |
| switch (c) { |
| case 'b': tracelimit = atoi(optarg); break; |
| case 'l': max_cycles = atoi(optarg); break; |
| case 'p': priority = atoi(optarg); break; |
| case 'q': quiet = 1; break; |
| case 't': num_threads = atoi(optarg); break; |
| case 'v': verbose = 1; break; |
| case '?': error = 1; break; |
| } |
| } |
| |
| if (priority < 0 || priority > 99) |
| error = 1; |
| |
| if (num_threads < 2) |
| error = 1; |
| |
| if (error) |
| display_help (); |
| } |
| |
| static void check_kernel(void) |
| { |
| size_t len; |
| char ver[256]; |
| int fd, maj, min, sub; |
| |
| fd = open("/proc/version", O_RDONLY, 0666); |
| len = read(fd, ver, 255); |
| close(fd); |
| ver[len-1] = 0x0; |
| sscanf(ver, "Linux version %d.%d.%d", &maj, &min, &sub); |
| if (maj == 2 && min == 6 && sub < 18) |
| oldtrace = 1; |
| } |
| |
| static void sighand(int sig) |
| { |
| shutdown = 1; |
| } |
| |
| static void print_stat(struct thread_param *par, int index, int verbose) |
| { |
| struct thread_stat *stat = par->stats; |
| |
| if (!verbose) { |
| if (quiet != 1) { |
| printf("T:%2d (%5d) P:%2d C:%7lu " |
| "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n", |
| index, stat->tid, par->prio, |
| stat->cycles, stat->min, stat->act, |
| stat->cycles ? |
| (long)(stat->avg/stat->cycles) : 0, stat->max); |
| } |
| } else { |
| while (stat->cycles != stat->cyclesread) { |
| long diff = stat->values[stat->cyclesread & par->bufmsk]; |
| printf("%8d:%8lu:%8ld\n", index, stat->cyclesread, diff); |
| stat->cyclesread++; |
| } |
| } |
| } |
| |
| int main(int argc, char **argv) |
| { |
| sigset_t sigset; |
| int signum = SIGUSR1; |
| struct thread_param *par; |
| struct thread_stat *stat; |
| int i, ret = -1; |
| |
| if (geteuid()) { |
| printf("need to run as root!\n"); |
| exit(-1); |
| } |
| |
| process_options(argc, argv); |
| |
| check_kernel(); |
| |
| sigemptyset(&sigset); |
| sigaddset(&sigset, signum); |
| sigprocmask (SIG_BLOCK, &sigset, NULL); |
| |
| signal(SIGINT, sighand); |
| signal(SIGTERM, sighand); |
| |
| par = calloc(num_threads, sizeof(struct thread_param)); |
| if (!par) |
| goto out; |
| stat = calloc(num_threads, sizeof(struct thread_stat)); |
| if (!stat) |
| goto outpar; |
| |
| for (i = 0; i < num_threads; i++) { |
| if (verbose) { |
| stat[i].values = calloc(VALBUF_SIZE, sizeof(long)); |
| if (!stat[i].values) |
| goto outall; |
| par[i].bufmsk = VALBUF_SIZE - 1; |
| } |
| |
| par[i].id = i; |
| par[i].prio = priority; |
| #if 0 |
| if (priority) |
| priority--; |
| #endif |
| par[i].signal = signum; |
| par[i].max_cycles = max_cycles; |
| par[i].stats = &stat[i]; |
| stat[i].min = 1000000; |
| stat[i].max = -1000000; |
| stat[i].avg = 0.0; |
| stat[i].threadstarted = 1; |
| pthread_create(&stat[i].thread, NULL, signalthread, &par[i]); |
| } |
| |
| while (!shutdown) { |
| int allstarted = 1; |
| |
| for (i = 0; i < num_threads; i++) { |
| if (stat[i].threadstarted != 2) |
| allstarted = 0; |
| } |
| if (!allstarted) |
| continue; |
| |
| for (i = 0; i < num_threads - 1; i++) |
| stat[i].tothread = stat[i+1].thread; |
| stat[i].tothread = stat[0].thread; |
| break; |
| } |
| pthread_kill(stat[0].thread, signum); |
| |
| while (!shutdown) { |
| char lavg[256]; |
| int fd, len, allstopped = 0; |
| |
| if (!verbose && !quiet) { |
| fd = open("/proc/loadavg", O_RDONLY, 0666); |
| len = read(fd, &lavg, 255); |
| close(fd); |
| lavg[len-1] = 0x0; |
| printf("%s \n\n", lavg); |
| } |
| |
| print_stat(&par[0], 0, verbose); |
| if(max_cycles && stat[0].cycles >= max_cycles) |
| allstopped++; |
| |
| usleep(10000); |
| if (shutdown || allstopped) |
| break; |
| if (!verbose && !quiet) |
| printf("\033[%dA", 3); |
| } |
| ret = 0; |
| outall: |
| shutdown = 1; |
| usleep(50000); |
| if (quiet) |
| quiet = 2; |
| for (i = 0; i < num_threads; i++) { |
| if (stat[i].threadstarted > 0) |
| pthread_kill(stat[i].thread, SIGTERM); |
| if (stat[i].threadstarted) { |
| pthread_join(stat[i].thread, NULL); |
| if (quiet) |
| print_stat(&par[i], i, 0); |
| } |
| if (stat[i].values) |
| free(stat[i].values); |
| } |
| free(stat); |
| outpar: |
| free(par); |
| out: |
| exit(ret); |
| } |