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