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