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