1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> 4 */ 5 6 #include <getopt.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <signal.h> 10 #include <unistd.h> 11 #include <stdio.h> 12 #include <time.h> 13 14 #include "utils.h" 15 #include "osnoise.h" 16 #include "timerlat.h" 17 18 struct timerlat_hist_params { 19 char *cpus; 20 char *monitored_cpus; 21 char *trace_output; 22 unsigned long long runtime; 23 long long stop_us; 24 long long stop_total_us; 25 long long timerlat_period_us; 26 long long print_stack; 27 int sleep_time; 28 int output_divisor; 29 int duration; 30 int set_sched; 31 int dma_latency; 32 struct sched_attr sched_param; 33 struct trace_events *events; 34 35 char no_irq; 36 char no_thread; 37 char no_header; 38 char no_summary; 39 char no_index; 40 char with_zeros; 41 int bucket_size; 42 int entries; 43 }; 44 45 struct timerlat_hist_cpu { 46 int *irq; 47 int *thread; 48 49 int irq_count; 50 int thread_count; 51 52 unsigned long long min_irq; 53 unsigned long long sum_irq; 54 unsigned long long max_irq; 55 56 unsigned long long min_thread; 57 unsigned long long sum_thread; 58 unsigned long long max_thread; 59 }; 60 61 struct timerlat_hist_data { 62 struct timerlat_hist_cpu *hist; 63 int entries; 64 int bucket_size; 65 int nr_cpus; 66 }; 67 68 /* 69 * timerlat_free_histogram - free runtime data 70 */ 71 static void 72 timerlat_free_histogram(struct timerlat_hist_data *data) 73 { 74 int cpu; 75 76 /* one histogram for IRQ and one for thread, per CPU */ 77 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 78 if (data->hist[cpu].irq) 79 free(data->hist[cpu].irq); 80 81 if (data->hist[cpu].thread) 82 free(data->hist[cpu].thread); 83 } 84 85 /* one set of histograms per CPU */ 86 if (data->hist) 87 free(data->hist); 88 89 free(data); 90 } 91 92 /* 93 * timerlat_alloc_histogram - alloc runtime data 94 */ 95 static struct timerlat_hist_data 96 *timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size) 97 { 98 struct timerlat_hist_data *data; 99 int cpu; 100 101 data = calloc(1, sizeof(*data)); 102 if (!data) 103 return NULL; 104 105 data->entries = entries; 106 data->bucket_size = bucket_size; 107 data->nr_cpus = nr_cpus; 108 109 /* one set of histograms per CPU */ 110 data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); 111 if (!data->hist) 112 goto cleanup; 113 114 /* one histogram for IRQ and one for thread, per cpu */ 115 for (cpu = 0; cpu < nr_cpus; cpu++) { 116 data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1)); 117 if (!data->hist[cpu].irq) 118 goto cleanup; 119 data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1)); 120 if (!data->hist[cpu].thread) 121 goto cleanup; 122 } 123 124 /* set the min to max */ 125 for (cpu = 0; cpu < nr_cpus; cpu++) { 126 data->hist[cpu].min_irq = ~0; 127 data->hist[cpu].min_thread = ~0; 128 } 129 130 return data; 131 132 cleanup: 133 timerlat_free_histogram(data); 134 return NULL; 135 } 136 137 /* 138 * timerlat_hist_update - record a new timerlat occurent on cpu, updating data 139 */ 140 static void 141 timerlat_hist_update(struct osnoise_tool *tool, int cpu, 142 unsigned long long thread, 143 unsigned long long latency) 144 { 145 struct timerlat_hist_params *params = tool->params; 146 struct timerlat_hist_data *data = tool->data; 147 int entries = data->entries; 148 int bucket; 149 int *hist; 150 151 if (params->output_divisor) 152 latency = latency / params->output_divisor; 153 154 if (data->bucket_size) 155 bucket = latency / data->bucket_size; 156 157 if (!thread) { 158 hist = data->hist[cpu].irq; 159 data->hist[cpu].irq_count++; 160 update_min(&data->hist[cpu].min_irq, &latency); 161 update_sum(&data->hist[cpu].sum_irq, &latency); 162 update_max(&data->hist[cpu].max_irq, &latency); 163 } else { 164 hist = data->hist[cpu].thread; 165 data->hist[cpu].thread_count++; 166 update_min(&data->hist[cpu].min_thread, &latency); 167 update_sum(&data->hist[cpu].sum_thread, &latency); 168 update_max(&data->hist[cpu].max_thread, &latency); 169 } 170 171 if (bucket < entries) 172 hist[bucket]++; 173 else 174 hist[entries]++; 175 } 176 177 /* 178 * timerlat_hist_handler - this is the handler for timerlat tracer events 179 */ 180 static int 181 timerlat_hist_handler(struct trace_seq *s, struct tep_record *record, 182 struct tep_event *event, void *data) 183 { 184 struct trace_instance *trace = data; 185 unsigned long long thread, latency; 186 struct osnoise_tool *tool; 187 int cpu = record->cpu; 188 189 tool = container_of(trace, struct osnoise_tool, trace); 190 191 tep_get_field_val(s, event, "context", record, &thread, 1); 192 tep_get_field_val(s, event, "timer_latency", record, &latency, 1); 193 194 timerlat_hist_update(tool, cpu, thread, latency); 195 196 return 0; 197 } 198 199 /* 200 * timerlat_hist_header - print the header of the tracer to the output 201 */ 202 static void timerlat_hist_header(struct osnoise_tool *tool) 203 { 204 struct timerlat_hist_params *params = tool->params; 205 struct timerlat_hist_data *data = tool->data; 206 struct trace_seq *s = tool->trace.seq; 207 char duration[26]; 208 int cpu; 209 210 if (params->no_header) 211 return; 212 213 get_duration(tool->start_time, duration, sizeof(duration)); 214 trace_seq_printf(s, "# RTLA timerlat histogram\n"); 215 trace_seq_printf(s, "# Time unit is %s (%s)\n", 216 params->output_divisor == 1 ? "nanoseconds" : "microseconds", 217 params->output_divisor == 1 ? "ns" : "us"); 218 219 trace_seq_printf(s, "# Duration: %s\n", duration); 220 221 if (!params->no_index) 222 trace_seq_printf(s, "Index"); 223 224 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 225 if (params->cpus && !params->monitored_cpus[cpu]) 226 continue; 227 228 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 229 continue; 230 231 if (!params->no_irq) 232 trace_seq_printf(s, " IRQ-%03d", cpu); 233 234 if (!params->no_thread) 235 trace_seq_printf(s, " Thr-%03d", cpu); 236 } 237 trace_seq_printf(s, "\n"); 238 239 240 trace_seq_do_printf(s); 241 trace_seq_reset(s); 242 } 243 244 /* 245 * timerlat_print_summary - print the summary of the hist data to the output 246 */ 247 static void 248 timerlat_print_summary(struct timerlat_hist_params *params, 249 struct trace_instance *trace, 250 struct timerlat_hist_data *data) 251 { 252 int cpu; 253 254 if (params->no_summary) 255 return; 256 257 if (!params->no_index) 258 trace_seq_printf(trace->seq, "count:"); 259 260 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 261 if (params->cpus && !params->monitored_cpus[cpu]) 262 continue; 263 264 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 265 continue; 266 267 if (!params->no_irq) 268 trace_seq_printf(trace->seq, "%9d ", 269 data->hist[cpu].irq_count); 270 271 if (!params->no_thread) 272 trace_seq_printf(trace->seq, "%9d ", 273 data->hist[cpu].thread_count); 274 } 275 trace_seq_printf(trace->seq, "\n"); 276 277 if (!params->no_index) 278 trace_seq_printf(trace->seq, "min: "); 279 280 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 281 if (params->cpus && !params->monitored_cpus[cpu]) 282 continue; 283 284 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 285 continue; 286 287 if (!params->no_irq) 288 trace_seq_printf(trace->seq, "%9llu ", 289 data->hist[cpu].min_irq); 290 291 if (!params->no_thread) 292 trace_seq_printf(trace->seq, "%9llu ", 293 data->hist[cpu].min_thread); 294 } 295 trace_seq_printf(trace->seq, "\n"); 296 297 if (!params->no_index) 298 trace_seq_printf(trace->seq, "avg: "); 299 300 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 301 if (params->cpus && !params->monitored_cpus[cpu]) 302 continue; 303 304 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 305 continue; 306 307 if (!params->no_irq) { 308 if (data->hist[cpu].irq_count) 309 trace_seq_printf(trace->seq, "%9llu ", 310 data->hist[cpu].sum_irq / data->hist[cpu].irq_count); 311 else 312 trace_seq_printf(trace->seq, " - "); 313 } 314 315 if (!params->no_thread) { 316 if (data->hist[cpu].thread_count) 317 trace_seq_printf(trace->seq, "%9llu ", 318 data->hist[cpu].sum_thread / data->hist[cpu].thread_count); 319 else 320 trace_seq_printf(trace->seq, " - "); 321 } 322 } 323 trace_seq_printf(trace->seq, "\n"); 324 325 if (!params->no_index) 326 trace_seq_printf(trace->seq, "max: "); 327 328 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 329 if (params->cpus && !params->monitored_cpus[cpu]) 330 continue; 331 332 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 333 continue; 334 335 if (!params->no_irq) 336 trace_seq_printf(trace->seq, "%9llu ", 337 data->hist[cpu].max_irq); 338 339 if (!params->no_thread) 340 trace_seq_printf(trace->seq, "%9llu ", 341 data->hist[cpu].max_thread); 342 } 343 trace_seq_printf(trace->seq, "\n"); 344 trace_seq_do_printf(trace->seq); 345 trace_seq_reset(trace->seq); 346 } 347 348 /* 349 * timerlat_print_stats - print data for all CPUs 350 */ 351 static void 352 timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool) 353 { 354 struct timerlat_hist_data *data = tool->data; 355 struct trace_instance *trace = &tool->trace; 356 int bucket, cpu; 357 int total; 358 359 timerlat_hist_header(tool); 360 361 for (bucket = 0; bucket < data->entries; bucket++) { 362 total = 0; 363 364 if (!params->no_index) 365 trace_seq_printf(trace->seq, "%-6d", 366 bucket * data->bucket_size); 367 368 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 369 if (params->cpus && !params->monitored_cpus[cpu]) 370 continue; 371 372 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 373 continue; 374 375 if (!params->no_irq) { 376 total += data->hist[cpu].irq[bucket]; 377 trace_seq_printf(trace->seq, "%9d ", 378 data->hist[cpu].irq[bucket]); 379 } 380 381 if (!params->no_thread) { 382 total += data->hist[cpu].thread[bucket]; 383 trace_seq_printf(trace->seq, "%9d ", 384 data->hist[cpu].thread[bucket]); 385 } 386 387 } 388 389 if (total == 0 && !params->with_zeros) { 390 trace_seq_reset(trace->seq); 391 continue; 392 } 393 394 trace_seq_printf(trace->seq, "\n"); 395 trace_seq_do_printf(trace->seq); 396 trace_seq_reset(trace->seq); 397 } 398 399 if (!params->no_index) 400 trace_seq_printf(trace->seq, "over: "); 401 402 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 403 if (params->cpus && !params->monitored_cpus[cpu]) 404 continue; 405 406 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 407 continue; 408 409 if (!params->no_irq) 410 trace_seq_printf(trace->seq, "%9d ", 411 data->hist[cpu].irq[data->entries]); 412 413 if (!params->no_thread) 414 trace_seq_printf(trace->seq, "%9d ", 415 data->hist[cpu].thread[data->entries]); 416 } 417 trace_seq_printf(trace->seq, "\n"); 418 trace_seq_do_printf(trace->seq); 419 trace_seq_reset(trace->seq); 420 421 timerlat_print_summary(params, trace, data); 422 } 423 424 /* 425 * timerlat_hist_usage - prints timerlat top usage message 426 */ 427 static void timerlat_hist_usage(char *usage) 428 { 429 int i; 430 431 char *msg[] = { 432 "", 433 " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", 434 " [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\", 435 " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", 436 " [--no-index] [--with-zeros] [--dma-latency us]", 437 "", 438 " -h/--help: print this menu", 439 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", 440 " -p/--period us: timerlat period in us", 441 " -i/--irq us: stop trace if the irq latency is higher than the argument in us", 442 " -T/--thread us: stop trace if the thread latency is higher than the argument in us", 443 " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", 444 " -c/--cpus cpus: run the tracer only on the given cpus", 445 " -d/--duration time[m|h|d]: duration of the session in seconds", 446 " -D/--debug: print debug info", 447 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]", 448 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", 449 " --filter <filter>: enable a trace event filter to the previous -e event", 450 " --trigger <trigger>: enable a trace event trigger to the previous -e event", 451 " -n/--nano: display data in nanoseconds", 452 " -b/--bucket-size N: set the histogram bucket size (default 1)", 453 " -E/--entries N: set the number of entries of the histogram (default 256)", 454 " --no-irq: ignore IRQ latencies", 455 " --no-thread: ignore thread latencies", 456 " --no-header: do not print header", 457 " --no-summary: do not print summary", 458 " --no-index: do not print index", 459 " --with-zeros: print zero only entries", 460 " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", 461 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", 462 " o:prio - use SCHED_OTHER with prio", 463 " r:prio - use SCHED_RR with prio", 464 " f:prio - use SCHED_FIFO with prio", 465 " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", 466 " in nanoseconds", 467 NULL, 468 }; 469 470 if (usage) 471 fprintf(stderr, "%s\n", usage); 472 473 fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n", 474 VERSION); 475 476 for (i = 0; msg[i]; i++) 477 fprintf(stderr, "%s\n", msg[i]); 478 exit(1); 479 } 480 481 /* 482 * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters 483 */ 484 static struct timerlat_hist_params 485 *timerlat_hist_parse_args(int argc, char *argv[]) 486 { 487 struct timerlat_hist_params *params; 488 struct trace_events *tevent; 489 int auto_thresh; 490 int retval; 491 int c; 492 493 params = calloc(1, sizeof(*params)); 494 if (!params) 495 exit(1); 496 497 /* disabled by default */ 498 params->dma_latency = -1; 499 500 /* display data in microseconds */ 501 params->output_divisor = 1000; 502 params->bucket_size = 1; 503 params->entries = 256; 504 505 while (1) { 506 static struct option long_options[] = { 507 {"auto", required_argument, 0, 'a'}, 508 {"cpus", required_argument, 0, 'c'}, 509 {"bucket-size", required_argument, 0, 'b'}, 510 {"debug", no_argument, 0, 'D'}, 511 {"entries", required_argument, 0, 'E'}, 512 {"duration", required_argument, 0, 'd'}, 513 {"help", no_argument, 0, 'h'}, 514 {"irq", required_argument, 0, 'i'}, 515 {"nano", no_argument, 0, 'n'}, 516 {"period", required_argument, 0, 'p'}, 517 {"priority", required_argument, 0, 'P'}, 518 {"stack", required_argument, 0, 's'}, 519 {"thread", required_argument, 0, 'T'}, 520 {"trace", optional_argument, 0, 't'}, 521 {"event", required_argument, 0, 'e'}, 522 {"no-irq", no_argument, 0, '0'}, 523 {"no-thread", no_argument, 0, '1'}, 524 {"no-header", no_argument, 0, '2'}, 525 {"no-summary", no_argument, 0, '3'}, 526 {"no-index", no_argument, 0, '4'}, 527 {"with-zeros", no_argument, 0, '5'}, 528 {"trigger", required_argument, 0, '6'}, 529 {"filter", required_argument, 0, '7'}, 530 {"dma-latency", required_argument, 0, '8'}, 531 {0, 0, 0, 0} 532 }; 533 534 /* getopt_long stores the option index here. */ 535 int option_index = 0; 536 537 c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhi:np:P:s:t::T:0123456:7:8:", 538 long_options, &option_index); 539 540 /* detect the end of the options. */ 541 if (c == -1) 542 break; 543 544 switch (c) { 545 case 'a': 546 auto_thresh = get_llong_from_str(optarg); 547 548 /* set thread stop to auto_thresh */ 549 params->stop_total_us = auto_thresh; 550 551 /* get stack trace */ 552 params->print_stack = auto_thresh; 553 554 /* set trace */ 555 params->trace_output = "timerlat_trace.txt"; 556 557 break; 558 case 'c': 559 retval = parse_cpu_list(optarg, ¶ms->monitored_cpus); 560 if (retval) 561 timerlat_hist_usage("\nInvalid -c cpu list\n"); 562 params->cpus = optarg; 563 break; 564 case 'b': 565 params->bucket_size = get_llong_from_str(optarg); 566 if ((params->bucket_size == 0) || (params->bucket_size >= 1000000)) 567 timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); 568 break; 569 case 'D': 570 config_debug = 1; 571 break; 572 case 'd': 573 params->duration = parse_seconds_duration(optarg); 574 if (!params->duration) 575 timerlat_hist_usage("Invalid -D duration\n"); 576 break; 577 case 'e': 578 tevent = trace_event_alloc(optarg); 579 if (!tevent) { 580 err_msg("Error alloc trace event"); 581 exit(EXIT_FAILURE); 582 } 583 584 if (params->events) 585 tevent->next = params->events; 586 587 params->events = tevent; 588 break; 589 case 'E': 590 params->entries = get_llong_from_str(optarg); 591 if ((params->entries < 10) || (params->entries > 9999999)) 592 timerlat_hist_usage("Entries must be > 10 and < 9999999\n"); 593 break; 594 case 'h': 595 case '?': 596 timerlat_hist_usage(NULL); 597 break; 598 case 'i': 599 params->stop_us = get_llong_from_str(optarg); 600 break; 601 case 'n': 602 params->output_divisor = 1; 603 break; 604 case 'p': 605 params->timerlat_period_us = get_llong_from_str(optarg); 606 if (params->timerlat_period_us > 1000000) 607 timerlat_hist_usage("Period longer than 1 s\n"); 608 break; 609 case 'P': 610 retval = parse_prio(optarg, ¶ms->sched_param); 611 if (retval == -1) 612 timerlat_hist_usage("Invalid -P priority"); 613 params->set_sched = 1; 614 break; 615 case 's': 616 params->print_stack = get_llong_from_str(optarg); 617 break; 618 case 'T': 619 params->stop_total_us = get_llong_from_str(optarg); 620 break; 621 case 't': 622 if (optarg) 623 /* skip = */ 624 params->trace_output = &optarg[1]; 625 else 626 params->trace_output = "timerlat_trace.txt"; 627 break; 628 case '0': /* no irq */ 629 params->no_irq = 1; 630 break; 631 case '1': /* no thread */ 632 params->no_thread = 1; 633 break; 634 case '2': /* no header */ 635 params->no_header = 1; 636 break; 637 case '3': /* no summary */ 638 params->no_summary = 1; 639 break; 640 case '4': /* no index */ 641 params->no_index = 1; 642 break; 643 case '5': /* with zeros */ 644 params->with_zeros = 1; 645 break; 646 case '6': /* trigger */ 647 if (params->events) { 648 retval = trace_event_add_trigger(params->events, optarg); 649 if (retval) { 650 err_msg("Error adding trigger %s\n", optarg); 651 exit(EXIT_FAILURE); 652 } 653 } else { 654 timerlat_hist_usage("--trigger requires a previous -e\n"); 655 } 656 break; 657 case '7': /* filter */ 658 if (params->events) { 659 retval = trace_event_add_filter(params->events, optarg); 660 if (retval) { 661 err_msg("Error adding filter %s\n", optarg); 662 exit(EXIT_FAILURE); 663 } 664 } else { 665 timerlat_hist_usage("--filter requires a previous -e\n"); 666 } 667 break; 668 case '8': 669 params->dma_latency = get_llong_from_str(optarg); 670 if (params->dma_latency < 0 || params->dma_latency > 10000) { 671 err_msg("--dma-latency needs to be >= 0 and < 10000"); 672 exit(EXIT_FAILURE); 673 } 674 break; 675 default: 676 timerlat_hist_usage("Invalid option"); 677 } 678 } 679 680 if (geteuid()) { 681 err_msg("rtla needs root permission\n"); 682 exit(EXIT_FAILURE); 683 } 684 685 if (params->no_irq && params->no_thread) 686 timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here"); 687 688 if (params->no_index && !params->with_zeros) 689 timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense"); 690 691 return params; 692 } 693 694 /* 695 * timerlat_hist_apply_config - apply the hist configs to the initialized tool 696 */ 697 static int 698 timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params) 699 { 700 int retval; 701 702 if (!params->sleep_time) 703 params->sleep_time = 1; 704 705 if (params->cpus) { 706 retval = osnoise_set_cpus(tool->context, params->cpus); 707 if (retval) { 708 err_msg("Failed to apply CPUs config\n"); 709 goto out_err; 710 } 711 } 712 713 if (params->stop_us) { 714 retval = osnoise_set_stop_us(tool->context, params->stop_us); 715 if (retval) { 716 err_msg("Failed to set stop us\n"); 717 goto out_err; 718 } 719 } 720 721 if (params->stop_total_us) { 722 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 723 if (retval) { 724 err_msg("Failed to set stop total us\n"); 725 goto out_err; 726 } 727 } 728 729 if (params->timerlat_period_us) { 730 retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us); 731 if (retval) { 732 err_msg("Failed to set timerlat period\n"); 733 goto out_err; 734 } 735 } 736 737 if (params->print_stack) { 738 retval = osnoise_set_print_stack(tool->context, params->print_stack); 739 if (retval) { 740 err_msg("Failed to set print stack\n"); 741 goto out_err; 742 } 743 } 744 745 return 0; 746 747 out_err: 748 return -1; 749 } 750 751 /* 752 * timerlat_init_hist - initialize a timerlat hist tool with parameters 753 */ 754 static struct osnoise_tool 755 *timerlat_init_hist(struct timerlat_hist_params *params) 756 { 757 struct osnoise_tool *tool; 758 int nr_cpus; 759 760 nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 761 762 tool = osnoise_init_tool("timerlat_hist"); 763 if (!tool) 764 return NULL; 765 766 tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size); 767 if (!tool->data) 768 goto out_err; 769 770 tool->params = params; 771 772 tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat", 773 timerlat_hist_handler, tool); 774 775 return tool; 776 777 out_err: 778 osnoise_destroy_tool(tool); 779 return NULL; 780 } 781 782 static int stop_tracing; 783 static void stop_hist(int sig) 784 { 785 stop_tracing = 1; 786 } 787 788 /* 789 * timerlat_hist_set_signals - handles the signal to stop the tool 790 */ 791 static void 792 timerlat_hist_set_signals(struct timerlat_hist_params *params) 793 { 794 signal(SIGINT, stop_hist); 795 if (params->duration) { 796 signal(SIGALRM, stop_hist); 797 alarm(params->duration); 798 } 799 } 800 801 int timerlat_hist_main(int argc, char *argv[]) 802 { 803 struct timerlat_hist_params *params; 804 struct osnoise_tool *record = NULL; 805 struct osnoise_tool *tool = NULL; 806 struct trace_instance *trace; 807 int dma_latency_fd = -1; 808 int return_value = 1; 809 int retval; 810 811 params = timerlat_hist_parse_args(argc, argv); 812 if (!params) 813 exit(1); 814 815 tool = timerlat_init_hist(params); 816 if (!tool) { 817 err_msg("Could not init osnoise hist\n"); 818 goto out_exit; 819 } 820 821 retval = timerlat_hist_apply_config(tool, params); 822 if (retval) { 823 err_msg("Could not apply config\n"); 824 goto out_free; 825 } 826 827 trace = &tool->trace; 828 829 retval = enable_timerlat(trace); 830 if (retval) { 831 err_msg("Failed to enable timerlat tracer\n"); 832 goto out_free; 833 } 834 835 if (params->set_sched) { 836 retval = set_comm_sched_attr("timerlat/", ¶ms->sched_param); 837 if (retval) { 838 err_msg("Failed to set sched parameters\n"); 839 goto out_free; 840 } 841 } 842 843 if (params->dma_latency >= 0) { 844 dma_latency_fd = set_cpu_dma_latency(params->dma_latency); 845 if (dma_latency_fd < 0) { 846 err_msg("Could not set /dev/cpu_dma_latency.\n"); 847 goto out_free; 848 } 849 } 850 851 trace_instance_start(trace); 852 853 if (params->trace_output) { 854 record = osnoise_init_trace_tool("timerlat"); 855 if (!record) { 856 err_msg("Failed to enable the trace instance\n"); 857 goto out_free; 858 } 859 860 if (params->events) { 861 retval = trace_events_enable(&record->trace, params->events); 862 if (retval) 863 goto out_hist; 864 } 865 866 trace_instance_start(&record->trace); 867 } 868 869 tool->start_time = time(NULL); 870 timerlat_hist_set_signals(params); 871 872 while (!stop_tracing) { 873 sleep(params->sleep_time); 874 875 retval = tracefs_iterate_raw_events(trace->tep, 876 trace->inst, 877 NULL, 878 0, 879 collect_registered_events, 880 trace); 881 if (retval < 0) { 882 err_msg("Error iterating on events\n"); 883 goto out_hist; 884 } 885 886 if (trace_is_off(&tool->trace, &record->trace)) 887 break; 888 } 889 890 timerlat_print_stats(params, tool); 891 892 return_value = 0; 893 894 if (trace_is_off(&tool->trace, &record->trace)) { 895 printf("rtla timerlat hit stop tracing\n"); 896 if (params->trace_output) { 897 printf(" Saving trace to %s\n", params->trace_output); 898 save_trace_to_file(record->trace.inst, params->trace_output); 899 } 900 } 901 902 out_hist: 903 if (dma_latency_fd >= 0) 904 close(dma_latency_fd); 905 trace_events_destroy(&record->trace, params->events); 906 params->events = NULL; 907 out_free: 908 timerlat_free_histogram(tool->data); 909 osnoise_destroy_tool(record); 910 osnoise_destroy_tool(tool); 911 free(params); 912 out_exit: 913 exit(return_value); 914 } 915