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