1/****************************************************************************** 2 * 3 * Copyright © International Business Machines Corp., 2006-2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * librttest.c 21 * 22 * DESCRIPTION 23 * A set of commonly used convenience functions for writing 24 * threaded realtime test cases. 25 * 26 * USAGE: 27 * To be included in testcases. 28 * 29 * AUTHOR 30 * Darren Hart <dvhltc@us.ibm.com> 31 * 32 * HISTORY 33 * 2006-Apr-26: Initial version by Darren Hart 34 * 2006-May-08: Added atomic_{inc,set,get}, thread struct, debug function, 35 * rt_init, buffered printing -- Vernon Mauery 36 * 2006-May-09: improved command line argument handling 37 * 2007-Jul-12: Added latency tracing functions and I/O helper functions 38 * -- Josh triplett 39 * 2008-Jan-10: Added RR thread support to tests -- Chirag Jog 40 * 41 *****************************************************************************/ 42 43#include <librttest.h> 44#include <libstats.h> 45 46#include <stdio.h> 47#include <stdlib.h> 48#include <signal.h> 49#include <time.h> 50#include <string.h> 51#include <pthread.h> 52#include <sched.h> 53#include <errno.h> 54#include <unistd.h> 55#include <getopt.h> 56#include <sys/prctl.h> 57#include <sys/stat.h> 58#include <sys/syscall.h> 59#include <sys/types.h> 60#include <sys/mman.h> 61#include <fcntl.h> 62#include <math.h> 63 64static LIST_HEAD(_threads); 65static atomic_t _thread_count = { -1 }; 66 67pthread_mutex_t _buffer_mutex; 68char *_print_buffer = NULL; 69int _print_buffer_offset = 0; 70int _dbg_lvl = 0; 71double pass_criteria; 72 73static int _use_pi = 1; 74 75/* function implementations */ 76void rt_help(void) 77{ 78 printf("librt standard options:\n"); 79 printf 80 (" -b(0,1) 1:enable buffered output, 0:diable buffered output\n"); 81 printf(" -p(0,1) 0:don't use pi mutexes, 1:use pi mutexes\n"); 82 printf(" -m use mlockall\n"); 83 printf 84 (" -v[0-4] 0:no debug, 1:DBG_ERR, 2:DBG_WARN, 3:DBG_INFO, 4:DBG_DEBUG\n"); 85 printf(" -s Enable saving stats data (default disabled)\n"); 86 printf(" -c Set pass criteria\n"); 87} 88 89/* Calibrate the busy work loop */ 90void calibrate_busyloop(void) 91{ 92 volatile int i = CALIBRATE_LOOPS; 93 nsec_t start, end; 94 95 start = rt_gettime(); 96 while (--i > 0) { 97 continue; 98 } 99 end = rt_gettime(); 100 101 iters_per_us = (CALIBRATE_LOOPS * NS_PER_US) / (end - start); 102} 103 104int rt_init_long(const char *options, const struct option *longopts, 105 int (*parse_arg) (int option, char *value), int argc, 106 char *argv[]) 107{ 108 const struct option *cur_opt; 109 int use_buffer = 1; 110 char *longopt_vals; 111 size_t i; 112 int c; 113 opterr = 0; 114 int mlock = 0; 115 char *all_options; 116 117 if (asprintf(&all_options, ":b:mp:v:sc:%s", options) == -1) { 118 fprintf(stderr, 119 "Failed to allocate string for option string\n"); 120 exit(1); 121 } 122 123 /* Check for duplicate options in optstring */ 124 for (i = 0; i < strlen(all_options); i++) { 125 char opt = all_options[i]; 126 127 if (opt == ':') 128 continue; 129 130 /* Search ahead */ 131 if (strchr(&all_options[i + 1], opt)) { 132 fprintf(stderr, 133 "Programmer error -- argument -%c already used at least twice\n", 134 opt); 135 exit(1); 136 } 137 } 138 139 /* Ensure each long options has a known unique short option in val. */ 140 longopt_vals = ""; 141 cur_opt = longopts; 142 while (cur_opt && cur_opt->name) { 143 if (cur_opt->flag) { 144 fprintf(stderr, "Programmer error -- argument --%s flag" 145 " is non-null\n", cur_opt->name); 146 exit(1); 147 } 148 if (!strchr(all_options, cur_opt->val)) { 149 fprintf(stderr, "Programmer error -- argument --%s " 150 "shortopt -%c wasn't listed in options (%s)\n", 151 cur_opt->name, cur_opt->val, all_options); 152 exit(1); 153 } 154 if (strchr(longopt_vals, cur_opt->val)) { 155 fprintf(stderr, "Programmer error -- argument --%s " 156 "shortopt -%c is used more than once\n", 157 cur_opt->name, cur_opt->val); 158 exit(1); 159 } 160 if (asprintf(&longopt_vals, "%s%c", longopt_vals, cur_opt->val) 161 < 0) { 162 perror("asprintf"); 163 exit(2); 164 } 165 cur_opt++; 166 } 167 168 while ((c = getopt_long(argc, argv, all_options, longopts, NULL)) != -1) { 169 switch (c) { 170 case 'c': 171 pass_criteria = atof(optarg); 172 break; 173 case 'b': 174 use_buffer = atoi(optarg); 175 break; 176 case 'p': 177 _use_pi = atoi(optarg); 178 break; 179 case 'm': 180 mlock = 1; 181 break; 182 case 'v': 183 _dbg_lvl = atoi(optarg); 184 break; 185 case 's': 186 save_stats = 1; 187 break; 188 case ':': 189 if (optopt == '-') 190 fprintf(stderr, "long option missing arg\n"); 191 else 192 fprintf(stderr, "option -%c: missing arg\n", 193 optopt); 194 parse_arg('h', optarg); /* Just to display usage */ 195 exit(1); /* Just in case. (should normally be done by usage()) */ 196 case '?': 197 if (optopt == '-') 198 fprintf(stderr, "unrecognized long option\n"); 199 else 200 fprintf(stderr, "option -%c not recognized\n", 201 optopt); 202 parse_arg('h', optarg); /* Just to display usage */ 203 exit(1); /* Just in case. (should normally be done by usage()) */ 204 default: 205 if (parse_arg && parse_arg(c, optarg)) 206 break; /* Application option */ 207 208 fprintf(stderr, 209 "Programmer error -- option -%c defined but not handled\n", 210 c); 211 exit(1); 212 } 213 } 214 if (!_use_pi) 215 printf 216 ("Priority Inheritance has been disabled for this run.\n"); 217 if (use_buffer) 218 buffer_init(); 219 if (mlock) { 220 if (mlockall(MCL_CURRENT | MCL_FUTURE)) { 221 perror("failed to lock memory\n"); 222 exit(1); 223 } 224 } 225 226 calibrate_busyloop(); 227 228 /* 229 * atexit() order matters here - buffer_print() will be called before 230 * buffer_fini(). 231 */ 232 atexit(buffer_fini); 233 atexit(buffer_print); 234 return 0; 235} 236 237int rt_init(const char *options, int (*parse_arg) (int option, char *value), 238 int argc, char *argv[]) 239{ 240 return rt_init_long(options, NULL, parse_arg, argc, argv); 241} 242 243void buffer_init(void) 244{ 245 _print_buffer = malloc(PRINT_BUFFER_SIZE); 246 if (!_print_buffer) 247 fprintf(stderr, 248 "insufficient memory for print buffer - printing directly to stderr\n"); 249 else 250 memset(_print_buffer, 0, PRINT_BUFFER_SIZE); 251} 252 253void buffer_print(void) 254{ 255 if (_print_buffer) { 256 fprintf(stderr, "%s", _print_buffer); 257 memset(_print_buffer, 0, PRINT_BUFFER_SIZE); 258 _print_buffer_offset = 0; 259 } 260} 261 262void buffer_fini(void) 263{ 264 if (_print_buffer) 265 free(_print_buffer); 266 _print_buffer = NULL; 267} 268 269void cleanup(int i) 270{ 271 printf("Test terminated with asynchronous signal\n"); 272 buffer_print(); 273 buffer_fini(); 274 if (i) 275 exit(i); 276} 277 278void setup() 279{ 280 signal(SIGINT, cleanup); 281 signal(SIGQUIT, cleanup); 282 signal(SIGTERM, cleanup); 283} 284 285int create_thread(void *(*func) (void *), void *arg, int prio, int policy) 286{ 287 struct sched_param param; 288 int id, ret; 289 struct thread *thread; 290 291 id = atomic_inc(&_thread_count); 292 293 thread = malloc(sizeof(struct thread)); 294 if (!thread) 295 return -1; 296 297 list_add_tail(&thread->_threads, &_threads); 298 pthread_cond_init(&thread->cond, NULL); // Accept the defaults 299 init_pi_mutex(&thread->mutex); 300 thread->id = id; 301 thread->priority = prio; 302 thread->policy = policy; 303 thread->flags = 0; 304 thread->arg = arg; 305 thread->func = func; 306 param.sched_priority = prio; 307 308 pthread_attr_init(&thread->attr); 309 pthread_attr_setinheritsched(&thread->attr, PTHREAD_EXPLICIT_SCHED); 310 pthread_attr_setschedpolicy(&thread->attr, thread->policy); 311 pthread_attr_setschedparam(&thread->attr, ¶m); 312 313 if ((ret = 314 pthread_create(&thread->pthread, &thread->attr, func, 315 (void *)thread))) { 316 printf("pthread_create failed: %d (%s)\n", ret, strerror(ret)); 317 list_del(&thread->_threads); 318 pthread_attr_destroy(&thread->attr); 319 free(thread); 320 return -1; 321 } 322 pthread_attr_destroy(&thread->attr); 323 324 return id; 325} 326 327int create_fifo_thread(void *(*func) (void *), void *arg, int prio) 328{ 329 return create_thread(func, arg, prio, SCHED_FIFO); 330} 331 332int create_rr_thread(void *(*func) (void *), void *arg, int prio) 333{ 334 return create_thread(func, arg, prio, SCHED_RR); 335} 336 337int create_other_thread(void *(*func) (void *), void *arg) 338{ 339 return create_thread(func, arg, 0, SCHED_OTHER); 340} 341 342int set_thread_priority(pthread_t pthread, int prio) 343{ 344 struct sched_param sched_param; 345 sched_param.sched_priority = prio; 346 int policy; 347 348 policy = (prio > 0) ? SCHED_FIFO : SCHED_OTHER; 349 350 return pthread_setschedparam(pthread, policy, &sched_param); 351} 352 353int set_priority(int prio) 354{ 355 struct sched_param sp; 356 int ret = 0; 357 358 sp.sched_priority = prio; 359 if (sched_setscheduler(0, SCHED_FIFO, &sp) != 0) { 360 perror("sched_setscheduler"); 361 ret = -1; 362 } 363 return ret; 364} 365 366void join_thread(int i) 367{ 368 struct thread *p, *t = NULL; 369 list_for_each_entry(p, &_threads, _threads) { 370 if (p->id == i) { 371 t = p; 372 break; 373 } 374 } 375 if (t) { 376 t->flags |= THREAD_QUIT; 377 if (t->pthread) 378 pthread_join(t->pthread, NULL); 379 list_del(&t->_threads); 380 } 381} 382 383void all_threads_quit(void) 384{ 385 struct thread *p; 386 list_for_each_entry(p, &_threads, _threads) { 387 p->flags |= THREAD_QUIT; 388 } 389} 390 391void join_threads(void) 392{ 393 all_threads_quit(); 394 struct thread *p, *t; 395 list_for_each_entry_safe(p, t, &_threads, _threads) { 396 if (p->pthread) 397 pthread_join(p->pthread, NULL); 398 list_del(&p->_threads); 399 } 400} 401 402struct thread *get_thread(int i) 403{ 404 struct thread *p; 405 list_for_each_entry(p, &_threads, _threads) { 406 if (p->id == i) { 407 return p; 408 } 409 } 410 return NULL; 411} 412 413void ts_minus(struct timespec *ts_end, struct timespec *ts_start, 414 struct timespec *ts_delta) 415{ 416 if (ts_end == NULL || ts_start == NULL || ts_delta == NULL) { 417 printf("ERROR in %s: one or more of the timespecs is NULL", 418 __FUNCTION__); 419 return; 420 } 421 422 ts_delta->tv_sec = ts_end->tv_sec - ts_start->tv_sec; 423 ts_delta->tv_nsec = ts_end->tv_nsec - ts_start->tv_nsec; 424 ts_normalize(ts_delta); 425} 426 427void ts_plus(struct timespec *ts_a, struct timespec *ts_b, 428 struct timespec *ts_sum) 429{ 430 if (ts_a == NULL || ts_b == NULL || ts_sum == NULL) { 431 printf("ERROR in %s: one or more of the timespecs is NULL", 432 __FUNCTION__); 433 return; 434 } 435 436 ts_sum->tv_sec = ts_a->tv_sec + ts_b->tv_sec; 437 ts_sum->tv_nsec = ts_a->tv_nsec + ts_b->tv_nsec; 438 ts_normalize(ts_sum); 439} 440 441void ts_normalize(struct timespec *ts) 442{ 443 if (ts == NULL) { 444 /* FIXME: write a real error logging system */ 445 printf("ERROR in %s: ts is NULL\n", __FUNCTION__); 446 return; 447 } 448 449 /* get the abs(nsec) < NS_PER_SEC */ 450 while (ts->tv_nsec > NS_PER_SEC) { 451 ts->tv_sec++; 452 ts->tv_nsec -= NS_PER_SEC; 453 } 454 while (ts->tv_nsec < -NS_PER_SEC) { 455 ts->tv_sec--; 456 ts->tv_nsec += NS_PER_SEC; 457 } 458 459 /* get the values to the same polarity */ 460 if (ts->tv_sec > 0 && ts->tv_nsec < 0) { 461 ts->tv_sec--; 462 ts->tv_nsec += NS_PER_SEC; 463 } 464 if (ts->tv_sec < 0 && ts->tv_nsec > 0) { 465 ts->tv_sec++; 466 ts->tv_nsec -= NS_PER_SEC; 467 } 468} 469 470int ts_to_nsec(struct timespec *ts, nsec_t * ns) 471{ 472 struct timespec t; 473 if (ts == NULL) { 474 /* FIXME: write a real error logging system */ 475 printf("ERROR in %s: ts is NULL\n", __FUNCTION__); 476 return -1; 477 } 478 t.tv_sec = ts->tv_sec; 479 t.tv_nsec = ts->tv_nsec; 480 ts_normalize(&t); 481 482 if (t.tv_sec <= 0 && t.tv_nsec < 0) { 483 printf("ERROR in %s: ts is negative\n", __FUNCTION__); 484 return -1; 485 } 486 487 *ns = (nsec_t) ts->tv_sec * NS_PER_SEC + ts->tv_nsec; 488 return 0; 489} 490 491void nsec_to_ts(nsec_t ns, struct timespec *ts) 492{ 493 if (ts == NULL) { 494 /* FIXME: write a real error logging system */ 495 printf("ERROR in %s: ts is NULL\n", __FUNCTION__); 496 return; 497 } 498 ts->tv_sec = ns / NS_PER_SEC; 499 ts->tv_nsec = ns % NS_PER_SEC; 500} 501 502/* return difference in microseconds */ 503unsigned long long tsc_minus(unsigned long long tsc_start, 504 unsigned long long tsc_end) 505{ 506 unsigned long long delta; 507 if (tsc_start <= tsc_end) 508 delta = tsc_end - tsc_start; 509 else { 510 delta = ULL_MAX - (tsc_end - tsc_start) + 1; 511 printf("TSC wrapped, delta=%llu\n", delta); 512 } 513 return delta; 514} 515 516void rt_nanosleep_until(nsec_t ns) 517{ 518 struct timespec ts_sleep, ts_rem; 519 int rc; 520 nsec_to_ts(ns, &ts_sleep); 521 rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts_sleep, 522 &ts_rem); 523 /* FIXME: when should we display the remainder ? */ 524 if (rc != 0) { 525 printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n", 526 (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec); 527 } 528} 529 530void rt_nanosleep(nsec_t ns) 531{ 532 struct timespec ts_sleep, ts_rem; 533 int rc; 534 nsec_to_ts(ns, &ts_sleep); 535 rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_sleep, &ts_rem); 536 /* FIXME: when should we display the remainder ? */ 537 if (rc != 0) { 538 printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n", 539 (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec); 540 } 541} 542 543nsec_t rt_gettime(void) 544{ 545 struct timespec ts; 546 nsec_t ns; 547 int rc; 548 549 rc = clock_gettime(CLOCK_MONOTONIC, &ts); 550 if (rc != 0) { 551 printf("ERROR in %s: clock_gettime() returned %d\n", 552 __FUNCTION__, rc); 553 perror("clock_gettime() failed"); 554 return 0; 555 } 556 557 ts_to_nsec(&ts, &ns); 558 return ns; 559} 560 561void *busy_work_ms(int ms) 562{ 563 busy_work_us(ms * US_PER_MS); 564 return NULL; 565} 566 567void *busy_work_us(int us) 568{ 569 volatile int i; 570 nsec_t start, now; 571 int delta; /* time in us */ 572 573 i = us * iters_per_us; 574 575 start = rt_gettime(); 576 while (--i > 0) { 577 continue; 578 } 579 now = rt_gettime(); 580 581 delta = (now - start) / NS_PER_US; 582 /* uncomment to tune to your machine */ 583 /* printf("busy_work_us requested: %dus actual: %dus\n", us, delta); */ 584 return NULL; 585} 586 587void init_pi_mutex(pthread_mutex_t * m) 588{ 589#if HAS_PRIORITY_INHERIT 590 pthread_mutexattr_t attr; 591 int ret; 592 int protocol; 593 594 if ((ret = pthread_mutexattr_init(&attr)) != 0) { 595 printf("Failed to init mutexattr: %d (%s)\n", ret, 596 strerror(ret)); 597 }; 598 if (_use_pi 599 && (ret = 600 pthread_mutexattr_setprotocol(&attr, 601 PTHREAD_PRIO_INHERIT)) != 0) { 602 printf("Can't set protocol prio inherit: %d (%s)\n", ret, 603 strerror(ret)); 604 } 605 if ((ret = pthread_mutexattr_getprotocol(&attr, &protocol)) != 0) { 606 printf("Can't get mutexattr protocol: %d (%s)\n", ret, 607 strerror(ret)); 608 } 609 if ((ret = pthread_mutex_init(m, &attr)) != 0) { 610 printf("Failed to init mutex: %d (%s)\n", ret, strerror(ret)); 611 } 612#endif 613 614 /* FIXME: does any of this need to be destroyed ? */ 615} 616 617/* Write the entirety of data. Complain if unable to do so. */ 618static void write_or_complain(int fd, const void *data, size_t len) 619{ 620 const char *remaining = data; 621 622 while (len > 0) { 623 ssize_t ret = write(fd, remaining, len); 624 if (ret <= 0) { 625 if (errno != EAGAIN && errno != EINTR) { 626 perror("write"); 627 return; 628 } 629 } else { 630 remaining += ret; 631 len -= ret; 632 } 633 } 634} 635 636/* Write the given data to the existing file specified by pathname. Complain 637 * if unable to do so. */ 638static void write_file(const char *pathname, const void *data, size_t len) 639{ 640 int fd = open(pathname, O_WRONLY); 641 if (fd < 0) { 642 printf("Failed to open file \"%s\": %d (%s)\n", 643 pathname, errno, strerror(errno)); 644 return; 645 } 646 647 write_or_complain(fd, data, len); 648 649 if (close(fd) < 0) { 650 printf("Failed to close file \"%s\": %d (%s)\n", 651 pathname, errno, strerror(errno)); 652 } 653} 654 655/* Write the given '\0'-terminated string to the existing file specified by 656 * pathname. Complain if unable to do so. */ 657static void write_string_to_file(const char *pathname, const char *string) 658{ 659 write_file(pathname, string, strlen(string)); 660} 661 662static void read_and_print(const char *pathname, int output_fd) 663{ 664 char data[4096]; 665 int fd = open(pathname, O_RDONLY); 666 if (fd < 0) { 667 printf("Failed to open file \"%s\": %d (%s)\n", 668 pathname, errno, strerror(errno)); 669 return; 670 } 671 672 while (1) { 673 ssize_t ret = read(fd, data, sizeof(data)); 674 if (ret < 0) { 675 if (errno != EAGAIN && errno != EINTR) { 676 printf 677 ("Failed to read from file \"%s\": %d (%s)\n", 678 pathname, errno, strerror(errno)); 679 break; 680 } 681 } else if (ret == 0) 682 break; 683 else 684 write_or_complain(output_fd, data, ret); 685 } 686 687 if (close(fd) < 0) { 688 printf("Failed to close file \"%s\": %d (%s)\n", 689 pathname, errno, strerror(errno)); 690 } 691} 692 693void latency_trace_enable(void) 694{ 695 printf("Enabling latency tracer.\n"); 696 write_string_to_file("/proc/sys/kernel/trace_use_raw_cycles", "1"); 697 write_string_to_file("/proc/sys/kernel/trace_all_cpus", "1"); 698 write_string_to_file("/proc/sys/kernel/trace_enabled", "1"); 699 write_string_to_file("/proc/sys/kernel/trace_freerunning", "1"); 700 write_string_to_file("/proc/sys/kernel/trace_print_on_crash", "0"); 701 write_string_to_file("/proc/sys/kernel/trace_user_triggered", "1"); 702 write_string_to_file("/proc/sys/kernel/trace_user_trigger_irq", "-1"); 703 write_string_to_file("/proc/sys/kernel/trace_verbose", "0"); 704 write_string_to_file("/proc/sys/kernel/preempt_thresh", "0"); 705 write_string_to_file("/proc/sys/kernel/wakeup_timing", "0"); 706 write_string_to_file("/proc/sys/kernel/mcount_enabled", "1"); 707 write_string_to_file("/proc/sys/kernel/preempt_max_latency", "0"); 708} 709 710#ifndef PR_SET_TRACING 711#define PR_SET_TRACING 0 712#endif 713 714void latency_trace_start(void) 715{ 716 if (prctl(PR_SET_TRACING, 1) < 0) 717 perror("Failed to start tracing"); 718} 719 720void latency_trace_stop(void) 721{ 722 if (prctl(PR_SET_TRACING, 0) < 0) 723 perror("Failed to stop tracing"); 724} 725 726void latency_trace_print(void) 727{ 728 read_and_print("/proc/latency_trace", STDOUT_FILENO); 729} 730