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 <sched.h> 15 #include <pthread.h> 16 17 #include "utils.h" 18 #include "osnoise.h" 19 #include "timerlat.h" 20 #include "timerlat_aa.h" 21 #include "timerlat_u.h" 22 23 struct timerlat_hist_params { 24 char *cpus; 25 cpu_set_t monitored_cpus; 26 char *trace_output; 27 char *cgroup_name; 28 unsigned long long runtime; 29 long long stop_us; 30 long long stop_total_us; 31 long long timerlat_period_us; 32 long long print_stack; 33 int sleep_time; 34 int output_divisor; 35 int duration; 36 int set_sched; 37 int dma_latency; 38 int cgroup; 39 int hk_cpus; 40 int no_aa; 41 int dump_tasks; 42 int user_workload; 43 int kernel_workload; 44 int user_hist; 45 cpu_set_t hk_cpu_set; 46 struct sched_attr sched_param; 47 struct trace_events *events; 48 char no_irq; 49 char no_thread; 50 char no_header; 51 char no_summary; 52 char no_index; 53 char with_zeros; 54 int bucket_size; 55 int entries; 56 int warmup; 57 }; 58 59 struct timerlat_hist_cpu { 60 int *irq; 61 int *thread; 62 int *user; 63 64 int irq_count; 65 int thread_count; 66 int user_count; 67 68 unsigned long long min_irq; 69 unsigned long long sum_irq; 70 unsigned long long max_irq; 71 72 unsigned long long min_thread; 73 unsigned long long sum_thread; 74 unsigned long long max_thread; 75 76 unsigned long long min_user; 77 unsigned long long sum_user; 78 unsigned long long max_user; 79 }; 80 81 struct timerlat_hist_data { 82 struct timerlat_hist_cpu *hist; 83 int entries; 84 int bucket_size; 85 int nr_cpus; 86 }; 87 88 /* 89 * timerlat_free_histogram - free runtime data 90 */ 91 static void 92 timerlat_free_histogram(struct timerlat_hist_data *data) 93 { 94 int cpu; 95 96 /* one histogram for IRQ and one for thread, per CPU */ 97 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 98 if (data->hist[cpu].irq) 99 free(data->hist[cpu].irq); 100 101 if (data->hist[cpu].thread) 102 free(data->hist[cpu].thread); 103 104 if (data->hist[cpu].user) 105 free(data->hist[cpu].user); 106 107 } 108 109 /* one set of histograms per CPU */ 110 if (data->hist) 111 free(data->hist); 112 113 free(data); 114 } 115 116 /* 117 * timerlat_alloc_histogram - alloc runtime data 118 */ 119 static struct timerlat_hist_data 120 *timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size) 121 { 122 struct timerlat_hist_data *data; 123 int cpu; 124 125 data = calloc(1, sizeof(*data)); 126 if (!data) 127 return NULL; 128 129 data->entries = entries; 130 data->bucket_size = bucket_size; 131 data->nr_cpus = nr_cpus; 132 133 /* one set of histograms per CPU */ 134 data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); 135 if (!data->hist) 136 goto cleanup; 137 138 /* one histogram for IRQ and one for thread, per cpu */ 139 for (cpu = 0; cpu < nr_cpus; cpu++) { 140 data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1)); 141 if (!data->hist[cpu].irq) 142 goto cleanup; 143 144 data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1)); 145 if (!data->hist[cpu].thread) 146 goto cleanup; 147 148 data->hist[cpu].user = calloc(1, sizeof(*data->hist->user) * (entries + 1)); 149 if (!data->hist[cpu].user) 150 goto cleanup; 151 } 152 153 /* set the min to max */ 154 for (cpu = 0; cpu < nr_cpus; cpu++) { 155 data->hist[cpu].min_irq = ~0; 156 data->hist[cpu].min_thread = ~0; 157 data->hist[cpu].min_user = ~0; 158 } 159 160 return data; 161 162 cleanup: 163 timerlat_free_histogram(data); 164 return NULL; 165 } 166 167 /* 168 * timerlat_hist_update - record a new timerlat occurent on cpu, updating data 169 */ 170 static void 171 timerlat_hist_update(struct osnoise_tool *tool, int cpu, 172 unsigned long long context, 173 unsigned long long latency) 174 { 175 struct timerlat_hist_params *params = tool->params; 176 struct timerlat_hist_data *data = tool->data; 177 int entries = data->entries; 178 int bucket; 179 int *hist; 180 181 if (params->output_divisor) 182 latency = latency / params->output_divisor; 183 184 bucket = latency / data->bucket_size; 185 186 if (!context) { 187 hist = data->hist[cpu].irq; 188 data->hist[cpu].irq_count++; 189 update_min(&data->hist[cpu].min_irq, &latency); 190 update_sum(&data->hist[cpu].sum_irq, &latency); 191 update_max(&data->hist[cpu].max_irq, &latency); 192 } else if (context == 1) { 193 hist = data->hist[cpu].thread; 194 data->hist[cpu].thread_count++; 195 update_min(&data->hist[cpu].min_thread, &latency); 196 update_sum(&data->hist[cpu].sum_thread, &latency); 197 update_max(&data->hist[cpu].max_thread, &latency); 198 } else { /* user */ 199 hist = data->hist[cpu].user; 200 data->hist[cpu].user_count++; 201 update_min(&data->hist[cpu].min_user, &latency); 202 update_sum(&data->hist[cpu].sum_user, &latency); 203 update_max(&data->hist[cpu].max_user, &latency); 204 } 205 206 if (bucket < entries) 207 hist[bucket]++; 208 else 209 hist[entries]++; 210 } 211 212 /* 213 * timerlat_hist_handler - this is the handler for timerlat tracer events 214 */ 215 static int 216 timerlat_hist_handler(struct trace_seq *s, struct tep_record *record, 217 struct tep_event *event, void *data) 218 { 219 struct trace_instance *trace = data; 220 unsigned long long context, latency; 221 struct osnoise_tool *tool; 222 int cpu = record->cpu; 223 224 tool = container_of(trace, struct osnoise_tool, trace); 225 226 tep_get_field_val(s, event, "context", record, &context, 1); 227 tep_get_field_val(s, event, "timer_latency", record, &latency, 1); 228 229 timerlat_hist_update(tool, cpu, context, latency); 230 231 return 0; 232 } 233 234 /* 235 * timerlat_hist_header - print the header of the tracer to the output 236 */ 237 static void timerlat_hist_header(struct osnoise_tool *tool) 238 { 239 struct timerlat_hist_params *params = tool->params; 240 struct timerlat_hist_data *data = tool->data; 241 struct trace_seq *s = tool->trace.seq; 242 char duration[26]; 243 int cpu; 244 245 if (params->no_header) 246 return; 247 248 get_duration(tool->start_time, duration, sizeof(duration)); 249 trace_seq_printf(s, "# RTLA timerlat histogram\n"); 250 trace_seq_printf(s, "# Time unit is %s (%s)\n", 251 params->output_divisor == 1 ? "nanoseconds" : "microseconds", 252 params->output_divisor == 1 ? "ns" : "us"); 253 254 trace_seq_printf(s, "# Duration: %s\n", duration); 255 256 if (!params->no_index) 257 trace_seq_printf(s, "Index"); 258 259 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 260 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 261 continue; 262 263 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 264 continue; 265 266 if (!params->no_irq) 267 trace_seq_printf(s, " IRQ-%03d", cpu); 268 269 if (!params->no_thread) 270 trace_seq_printf(s, " Thr-%03d", cpu); 271 272 if (params->user_hist) 273 trace_seq_printf(s, " Usr-%03d", cpu); 274 } 275 trace_seq_printf(s, "\n"); 276 277 278 trace_seq_do_printf(s); 279 trace_seq_reset(s); 280 } 281 282 /* 283 * timerlat_print_summary - print the summary of the hist data to the output 284 */ 285 static void 286 timerlat_print_summary(struct timerlat_hist_params *params, 287 struct trace_instance *trace, 288 struct timerlat_hist_data *data) 289 { 290 int cpu; 291 292 if (params->no_summary) 293 return; 294 295 if (!params->no_index) 296 trace_seq_printf(trace->seq, "count:"); 297 298 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 299 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 300 continue; 301 302 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 303 continue; 304 305 if (!params->no_irq) 306 trace_seq_printf(trace->seq, "%9d ", 307 data->hist[cpu].irq_count); 308 309 if (!params->no_thread) 310 trace_seq_printf(trace->seq, "%9d ", 311 data->hist[cpu].thread_count); 312 313 if (params->user_hist) 314 trace_seq_printf(trace->seq, "%9d ", 315 data->hist[cpu].user_count); 316 } 317 trace_seq_printf(trace->seq, "\n"); 318 319 if (!params->no_index) 320 trace_seq_printf(trace->seq, "min: "); 321 322 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 323 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 324 continue; 325 326 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 327 continue; 328 329 if (!params->no_irq) 330 trace_seq_printf(trace->seq, "%9llu ", 331 data->hist[cpu].min_irq); 332 333 if (!params->no_thread) 334 trace_seq_printf(trace->seq, "%9llu ", 335 data->hist[cpu].min_thread); 336 337 if (params->user_hist) 338 trace_seq_printf(trace->seq, "%9llu ", 339 data->hist[cpu].min_user); 340 } 341 trace_seq_printf(trace->seq, "\n"); 342 343 if (!params->no_index) 344 trace_seq_printf(trace->seq, "avg: "); 345 346 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 347 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 348 continue; 349 350 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 351 continue; 352 353 if (!params->no_irq) { 354 if (data->hist[cpu].irq_count) 355 trace_seq_printf(trace->seq, "%9llu ", 356 data->hist[cpu].sum_irq / data->hist[cpu].irq_count); 357 else 358 trace_seq_printf(trace->seq, " - "); 359 } 360 361 if (!params->no_thread) { 362 if (data->hist[cpu].thread_count) 363 trace_seq_printf(trace->seq, "%9llu ", 364 data->hist[cpu].sum_thread / data->hist[cpu].thread_count); 365 else 366 trace_seq_printf(trace->seq, " - "); 367 } 368 369 if (params->user_hist) { 370 if (data->hist[cpu].user_count) 371 trace_seq_printf(trace->seq, "%9llu ", 372 data->hist[cpu].sum_user / data->hist[cpu].user_count); 373 else 374 trace_seq_printf(trace->seq, " - "); 375 } 376 } 377 trace_seq_printf(trace->seq, "\n"); 378 379 if (!params->no_index) 380 trace_seq_printf(trace->seq, "max: "); 381 382 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 383 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 384 continue; 385 386 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 387 continue; 388 389 if (!params->no_irq) 390 trace_seq_printf(trace->seq, "%9llu ", 391 data->hist[cpu].max_irq); 392 393 if (!params->no_thread) 394 trace_seq_printf(trace->seq, "%9llu ", 395 data->hist[cpu].max_thread); 396 397 if (params->user_hist) 398 trace_seq_printf(trace->seq, "%9llu ", 399 data->hist[cpu].max_user); 400 } 401 trace_seq_printf(trace->seq, "\n"); 402 trace_seq_do_printf(trace->seq); 403 trace_seq_reset(trace->seq); 404 } 405 406 static void 407 timerlat_print_stats_all(struct timerlat_hist_params *params, 408 struct trace_instance *trace, 409 struct timerlat_hist_data *data) 410 { 411 struct timerlat_hist_cpu *cpu_data; 412 struct timerlat_hist_cpu sum; 413 int cpu; 414 415 if (params->no_summary) 416 return; 417 418 memset(&sum, 0, sizeof(sum)); 419 sum.min_irq = ~0; 420 sum.min_thread = ~0; 421 sum.min_user = ~0; 422 423 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 424 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 425 continue; 426 427 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 428 continue; 429 430 cpu_data = &data->hist[cpu]; 431 432 sum.irq_count += cpu_data->irq_count; 433 update_min(&sum.min_irq, &cpu_data->min_irq); 434 update_sum(&sum.sum_irq, &cpu_data->sum_irq); 435 update_max(&sum.max_irq, &cpu_data->max_irq); 436 437 sum.thread_count += cpu_data->thread_count; 438 update_min(&sum.min_thread, &cpu_data->min_thread); 439 update_sum(&sum.sum_thread, &cpu_data->sum_thread); 440 update_max(&sum.max_thread, &cpu_data->max_thread); 441 442 sum.user_count += cpu_data->user_count; 443 update_min(&sum.min_user, &cpu_data->min_user); 444 update_sum(&sum.sum_user, &cpu_data->sum_user); 445 update_max(&sum.max_user, &cpu_data->max_user); 446 } 447 448 if (!params->no_index) 449 trace_seq_printf(trace->seq, "ALL: "); 450 451 if (!params->no_irq) 452 trace_seq_printf(trace->seq, " IRQ"); 453 454 if (!params->no_thread) 455 trace_seq_printf(trace->seq, " Thr"); 456 457 if (params->user_hist) 458 trace_seq_printf(trace->seq, " Usr"); 459 460 trace_seq_printf(trace->seq, "\n"); 461 462 if (!params->no_index) 463 trace_seq_printf(trace->seq, "count:"); 464 465 if (!params->no_irq) 466 trace_seq_printf(trace->seq, "%9d ", 467 sum.irq_count); 468 469 if (!params->no_thread) 470 trace_seq_printf(trace->seq, "%9d ", 471 sum.thread_count); 472 473 if (params->user_hist) 474 trace_seq_printf(trace->seq, "%9d ", 475 sum.user_count); 476 477 trace_seq_printf(trace->seq, "\n"); 478 479 if (!params->no_index) 480 trace_seq_printf(trace->seq, "min: "); 481 482 if (!params->no_irq) 483 trace_seq_printf(trace->seq, "%9llu ", 484 sum.min_irq); 485 486 if (!params->no_thread) 487 trace_seq_printf(trace->seq, "%9llu ", 488 sum.min_thread); 489 490 if (params->user_hist) 491 trace_seq_printf(trace->seq, "%9llu ", 492 sum.min_user); 493 494 trace_seq_printf(trace->seq, "\n"); 495 496 if (!params->no_index) 497 trace_seq_printf(trace->seq, "avg: "); 498 499 if (!params->no_irq) 500 trace_seq_printf(trace->seq, "%9llu ", 501 sum.sum_irq / sum.irq_count); 502 503 if (!params->no_thread) 504 trace_seq_printf(trace->seq, "%9llu ", 505 sum.sum_thread / sum.thread_count); 506 507 if (params->user_hist) 508 trace_seq_printf(trace->seq, "%9llu ", 509 sum.sum_user / sum.user_count); 510 511 trace_seq_printf(trace->seq, "\n"); 512 513 if (!params->no_index) 514 trace_seq_printf(trace->seq, "max: "); 515 516 if (!params->no_irq) 517 trace_seq_printf(trace->seq, "%9llu ", 518 sum.max_irq); 519 520 if (!params->no_thread) 521 trace_seq_printf(trace->seq, "%9llu ", 522 sum.max_thread); 523 524 if (params->user_hist) 525 trace_seq_printf(trace->seq, "%9llu ", 526 sum.max_user); 527 528 trace_seq_printf(trace->seq, "\n"); 529 trace_seq_do_printf(trace->seq); 530 trace_seq_reset(trace->seq); 531 } 532 533 /* 534 * timerlat_print_stats - print data for each CPUs 535 */ 536 static void 537 timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool) 538 { 539 struct timerlat_hist_data *data = tool->data; 540 struct trace_instance *trace = &tool->trace; 541 int bucket, cpu; 542 int total; 543 544 timerlat_hist_header(tool); 545 546 for (bucket = 0; bucket < data->entries; bucket++) { 547 total = 0; 548 549 if (!params->no_index) 550 trace_seq_printf(trace->seq, "%-6d", 551 bucket * data->bucket_size); 552 553 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 554 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 555 continue; 556 557 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 558 continue; 559 560 if (!params->no_irq) { 561 total += data->hist[cpu].irq[bucket]; 562 trace_seq_printf(trace->seq, "%9d ", 563 data->hist[cpu].irq[bucket]); 564 } 565 566 if (!params->no_thread) { 567 total += data->hist[cpu].thread[bucket]; 568 trace_seq_printf(trace->seq, "%9d ", 569 data->hist[cpu].thread[bucket]); 570 } 571 572 if (params->user_hist) { 573 total += data->hist[cpu].user[bucket]; 574 trace_seq_printf(trace->seq, "%9d ", 575 data->hist[cpu].user[bucket]); 576 } 577 578 } 579 580 if (total == 0 && !params->with_zeros) { 581 trace_seq_reset(trace->seq); 582 continue; 583 } 584 585 trace_seq_printf(trace->seq, "\n"); 586 trace_seq_do_printf(trace->seq); 587 trace_seq_reset(trace->seq); 588 } 589 590 if (!params->no_index) 591 trace_seq_printf(trace->seq, "over: "); 592 593 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 594 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 595 continue; 596 597 if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) 598 continue; 599 600 if (!params->no_irq) 601 trace_seq_printf(trace->seq, "%9d ", 602 data->hist[cpu].irq[data->entries]); 603 604 if (!params->no_thread) 605 trace_seq_printf(trace->seq, "%9d ", 606 data->hist[cpu].thread[data->entries]); 607 608 if (params->user_hist) 609 trace_seq_printf(trace->seq, "%9d ", 610 data->hist[cpu].user[data->entries]); 611 } 612 trace_seq_printf(trace->seq, "\n"); 613 trace_seq_do_printf(trace->seq); 614 trace_seq_reset(trace->seq); 615 616 timerlat_print_summary(params, trace, data); 617 timerlat_print_stats_all(params, trace, data); 618 } 619 620 /* 621 * timerlat_hist_usage - prints timerlat top usage message 622 */ 623 static void timerlat_hist_usage(char *usage) 624 { 625 int i; 626 627 char *msg[] = { 628 "", 629 " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", 630 " [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", 631 " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", 632 " [--no-index] [--with-zeros] [--dma-latency us] [-C[=cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", 633 " [--warm-up s]", 634 "", 635 " -h/--help: print this menu", 636 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", 637 " -p/--period us: timerlat period in us", 638 " -i/--irq us: stop trace if the irq latency is higher than the argument in us", 639 " -T/--thread us: stop trace if the thread latency is higher than the argument in us", 640 " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", 641 " -c/--cpus cpus: run the tracer only on the given cpus", 642 " -H/--house-keeping cpus: run rtla control threads only on the given cpus", 643 " -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", 644 " -d/--duration time[m|h|d]: duration of the session in seconds", 645 " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", 646 " -D/--debug: print debug info", 647 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]", 648 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", 649 " --filter <filter>: enable a trace event filter to the previous -e event", 650 " --trigger <trigger>: enable a trace event trigger to the previous -e event", 651 " -n/--nano: display data in nanoseconds", 652 " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", 653 " -b/--bucket-size N: set the histogram bucket size (default 1)", 654 " -E/--entries N: set the number of entries of the histogram (default 256)", 655 " --no-irq: ignore IRQ latencies", 656 " --no-thread: ignore thread latencies", 657 " --no-header: do not print header", 658 " --no-summary: do not print summary", 659 " --no-index: do not print index", 660 " --with-zeros: print zero only entries", 661 " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", 662 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", 663 " o:prio - use SCHED_OTHER with prio", 664 " r:prio - use SCHED_RR with prio", 665 " f:prio - use SCHED_FIFO with prio", 666 " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", 667 " in nanoseconds", 668 " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", 669 " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", 670 " -U/--user-load: enable timerlat for user-defined user-space workload", 671 " --warm-up s: let the workload run for s seconds before collecting data", 672 NULL, 673 }; 674 675 if (usage) 676 fprintf(stderr, "%s\n", usage); 677 678 fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n", 679 VERSION); 680 681 for (i = 0; msg[i]; i++) 682 fprintf(stderr, "%s\n", msg[i]); 683 684 if (usage) 685 exit(EXIT_FAILURE); 686 687 exit(EXIT_SUCCESS); 688 } 689 690 /* 691 * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters 692 */ 693 static struct timerlat_hist_params 694 *timerlat_hist_parse_args(int argc, char *argv[]) 695 { 696 struct timerlat_hist_params *params; 697 struct trace_events *tevent; 698 int auto_thresh; 699 int retval; 700 int c; 701 702 params = calloc(1, sizeof(*params)); 703 if (!params) 704 exit(1); 705 706 /* disabled by default */ 707 params->dma_latency = -1; 708 709 /* display data in microseconds */ 710 params->output_divisor = 1000; 711 params->bucket_size = 1; 712 params->entries = 256; 713 714 while (1) { 715 static struct option long_options[] = { 716 {"auto", required_argument, 0, 'a'}, 717 {"cpus", required_argument, 0, 'c'}, 718 {"cgroup", optional_argument, 0, 'C'}, 719 {"bucket-size", required_argument, 0, 'b'}, 720 {"debug", no_argument, 0, 'D'}, 721 {"entries", required_argument, 0, 'E'}, 722 {"duration", required_argument, 0, 'd'}, 723 {"house-keeping", required_argument, 0, 'H'}, 724 {"help", no_argument, 0, 'h'}, 725 {"irq", required_argument, 0, 'i'}, 726 {"nano", no_argument, 0, 'n'}, 727 {"period", required_argument, 0, 'p'}, 728 {"priority", required_argument, 0, 'P'}, 729 {"stack", required_argument, 0, 's'}, 730 {"thread", required_argument, 0, 'T'}, 731 {"trace", optional_argument, 0, 't'}, 732 {"user-threads", no_argument, 0, 'u'}, 733 {"kernel-threads", no_argument, 0, 'k'}, 734 {"user-load", no_argument, 0, 'U'}, 735 {"event", required_argument, 0, 'e'}, 736 {"no-irq", no_argument, 0, '0'}, 737 {"no-thread", no_argument, 0, '1'}, 738 {"no-header", no_argument, 0, '2'}, 739 {"no-summary", no_argument, 0, '3'}, 740 {"no-index", no_argument, 0, '4'}, 741 {"with-zeros", no_argument, 0, '5'}, 742 {"trigger", required_argument, 0, '6'}, 743 {"filter", required_argument, 0, '7'}, 744 {"dma-latency", required_argument, 0, '8'}, 745 {"no-aa", no_argument, 0, '9'}, 746 {"dump-task", no_argument, 0, '\1'}, 747 {"warm-up", required_argument, 0, '\2'}, 748 {0, 0, 0, 0} 749 }; 750 751 /* getopt_long stores the option index here. */ 752 int option_index = 0; 753 754 c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:knp:P:s:t::T:uU0123456:7:8:9\1\2:", 755 long_options, &option_index); 756 757 /* detect the end of the options. */ 758 if (c == -1) 759 break; 760 761 switch (c) { 762 case 'a': 763 auto_thresh = get_llong_from_str(optarg); 764 765 /* set thread stop to auto_thresh */ 766 params->stop_total_us = auto_thresh; 767 params->stop_us = auto_thresh; 768 769 /* get stack trace */ 770 params->print_stack = auto_thresh; 771 772 /* set trace */ 773 params->trace_output = "timerlat_trace.txt"; 774 775 break; 776 case 'c': 777 retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); 778 if (retval) 779 timerlat_hist_usage("\nInvalid -c cpu list\n"); 780 params->cpus = optarg; 781 break; 782 case 'C': 783 params->cgroup = 1; 784 if (!optarg) { 785 /* will inherit this cgroup */ 786 params->cgroup_name = NULL; 787 } else if (*optarg == '=') { 788 /* skip the = */ 789 params->cgroup_name = ++optarg; 790 } 791 break; 792 case 'b': 793 params->bucket_size = get_llong_from_str(optarg); 794 if ((params->bucket_size == 0) || (params->bucket_size >= 1000000)) 795 timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); 796 break; 797 case 'D': 798 config_debug = 1; 799 break; 800 case 'd': 801 params->duration = parse_seconds_duration(optarg); 802 if (!params->duration) 803 timerlat_hist_usage("Invalid -D duration\n"); 804 break; 805 case 'e': 806 tevent = trace_event_alloc(optarg); 807 if (!tevent) { 808 err_msg("Error alloc trace event"); 809 exit(EXIT_FAILURE); 810 } 811 812 if (params->events) 813 tevent->next = params->events; 814 815 params->events = tevent; 816 break; 817 case 'E': 818 params->entries = get_llong_from_str(optarg); 819 if ((params->entries < 10) || (params->entries > 9999999)) 820 timerlat_hist_usage("Entries must be > 10 and < 9999999\n"); 821 break; 822 case 'h': 823 case '?': 824 timerlat_hist_usage(NULL); 825 break; 826 case 'H': 827 params->hk_cpus = 1; 828 retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); 829 if (retval) { 830 err_msg("Error parsing house keeping CPUs\n"); 831 exit(EXIT_FAILURE); 832 } 833 break; 834 case 'i': 835 params->stop_us = get_llong_from_str(optarg); 836 break; 837 case 'k': 838 params->kernel_workload = 1; 839 break; 840 case 'n': 841 params->output_divisor = 1; 842 break; 843 case 'p': 844 params->timerlat_period_us = get_llong_from_str(optarg); 845 if (params->timerlat_period_us > 1000000) 846 timerlat_hist_usage("Period longer than 1 s\n"); 847 break; 848 case 'P': 849 retval = parse_prio(optarg, ¶ms->sched_param); 850 if (retval == -1) 851 timerlat_hist_usage("Invalid -P priority"); 852 params->set_sched = 1; 853 break; 854 case 's': 855 params->print_stack = get_llong_from_str(optarg); 856 break; 857 case 'T': 858 params->stop_total_us = get_llong_from_str(optarg); 859 break; 860 case 't': 861 if (optarg) 862 /* skip = */ 863 params->trace_output = &optarg[1]; 864 else 865 params->trace_output = "timerlat_trace.txt"; 866 break; 867 case 'u': 868 params->user_workload = 1; 869 /* fallback: -u implies in -U */ 870 case 'U': 871 params->user_hist = 1; 872 break; 873 case '0': /* no irq */ 874 params->no_irq = 1; 875 break; 876 case '1': /* no thread */ 877 params->no_thread = 1; 878 break; 879 case '2': /* no header */ 880 params->no_header = 1; 881 break; 882 case '3': /* no summary */ 883 params->no_summary = 1; 884 break; 885 case '4': /* no index */ 886 params->no_index = 1; 887 break; 888 case '5': /* with zeros */ 889 params->with_zeros = 1; 890 break; 891 case '6': /* trigger */ 892 if (params->events) { 893 retval = trace_event_add_trigger(params->events, optarg); 894 if (retval) { 895 err_msg("Error adding trigger %s\n", optarg); 896 exit(EXIT_FAILURE); 897 } 898 } else { 899 timerlat_hist_usage("--trigger requires a previous -e\n"); 900 } 901 break; 902 case '7': /* filter */ 903 if (params->events) { 904 retval = trace_event_add_filter(params->events, optarg); 905 if (retval) { 906 err_msg("Error adding filter %s\n", optarg); 907 exit(EXIT_FAILURE); 908 } 909 } else { 910 timerlat_hist_usage("--filter requires a previous -e\n"); 911 } 912 break; 913 case '8': 914 params->dma_latency = get_llong_from_str(optarg); 915 if (params->dma_latency < 0 || params->dma_latency > 10000) { 916 err_msg("--dma-latency needs to be >= 0 and < 10000"); 917 exit(EXIT_FAILURE); 918 } 919 break; 920 case '9': 921 params->no_aa = 1; 922 break; 923 case '\1': 924 params->dump_tasks = 1; 925 break; 926 case '\2': 927 params->warmup = get_llong_from_str(optarg); 928 break; 929 default: 930 timerlat_hist_usage("Invalid option"); 931 } 932 } 933 934 if (geteuid()) { 935 err_msg("rtla needs root permission\n"); 936 exit(EXIT_FAILURE); 937 } 938 939 if (params->no_irq && params->no_thread) 940 timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here"); 941 942 if (params->no_index && !params->with_zeros) 943 timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense"); 944 945 /* 946 * Auto analysis only happens if stop tracing, thus: 947 */ 948 if (!params->stop_us && !params->stop_total_us) 949 params->no_aa = 1; 950 951 if (params->kernel_workload && params->user_workload) 952 timerlat_hist_usage("--kernel-threads and --user-threads are mutually exclusive!"); 953 954 return params; 955 } 956 957 /* 958 * timerlat_hist_apply_config - apply the hist configs to the initialized tool 959 */ 960 static int 961 timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params) 962 { 963 int retval, i; 964 965 if (!params->sleep_time) 966 params->sleep_time = 1; 967 968 if (params->cpus) { 969 retval = osnoise_set_cpus(tool->context, params->cpus); 970 if (retval) { 971 err_msg("Failed to apply CPUs config\n"); 972 goto out_err; 973 } 974 } else { 975 for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) 976 CPU_SET(i, ¶ms->monitored_cpus); 977 } 978 979 if (params->stop_us) { 980 retval = osnoise_set_stop_us(tool->context, params->stop_us); 981 if (retval) { 982 err_msg("Failed to set stop us\n"); 983 goto out_err; 984 } 985 } 986 987 if (params->stop_total_us) { 988 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 989 if (retval) { 990 err_msg("Failed to set stop total us\n"); 991 goto out_err; 992 } 993 } 994 995 if (params->timerlat_period_us) { 996 retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us); 997 if (retval) { 998 err_msg("Failed to set timerlat period\n"); 999 goto out_err; 1000 } 1001 } 1002 1003 if (params->print_stack) { 1004 retval = osnoise_set_print_stack(tool->context, params->print_stack); 1005 if (retval) { 1006 err_msg("Failed to set print stack\n"); 1007 goto out_err; 1008 } 1009 } 1010 1011 if (params->hk_cpus) { 1012 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), 1013 ¶ms->hk_cpu_set); 1014 if (retval == -1) { 1015 err_msg("Failed to set rtla to the house keeping CPUs\n"); 1016 goto out_err; 1017 } 1018 } else if (params->cpus) { 1019 /* 1020 * Even if the user do not set a house-keeping CPU, try to 1021 * move rtla to a CPU set different to the one where the user 1022 * set the workload to run. 1023 * 1024 * No need to check results as this is an automatic attempt. 1025 */ 1026 auto_house_keeping(¶ms->monitored_cpus); 1027 } 1028 1029 /* 1030 * If the user did not specify a type of thread, try user-threads first. 1031 * Fall back to kernel threads otherwise. 1032 */ 1033 if (!params->kernel_workload && !params->user_workload) { 1034 retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd"); 1035 if (retval) { 1036 debug_msg("User-space interface detected, setting user-threads\n"); 1037 params->user_workload = 1; 1038 params->user_hist = 1; 1039 } else { 1040 debug_msg("User-space interface not detected, setting kernel-threads\n"); 1041 params->kernel_workload = 1; 1042 } 1043 } 1044 1045 if (params->user_hist) { 1046 retval = osnoise_set_workload(tool->context, 0); 1047 if (retval) { 1048 err_msg("Failed to set OSNOISE_WORKLOAD option\n"); 1049 goto out_err; 1050 } 1051 } 1052 1053 return 0; 1054 1055 out_err: 1056 return -1; 1057 } 1058 1059 /* 1060 * timerlat_init_hist - initialize a timerlat hist tool with parameters 1061 */ 1062 static struct osnoise_tool 1063 *timerlat_init_hist(struct timerlat_hist_params *params) 1064 { 1065 struct osnoise_tool *tool; 1066 int nr_cpus; 1067 1068 nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 1069 1070 tool = osnoise_init_tool("timerlat_hist"); 1071 if (!tool) 1072 return NULL; 1073 1074 tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size); 1075 if (!tool->data) 1076 goto out_err; 1077 1078 tool->params = params; 1079 1080 tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat", 1081 timerlat_hist_handler, tool); 1082 1083 return tool; 1084 1085 out_err: 1086 osnoise_destroy_tool(tool); 1087 return NULL; 1088 } 1089 1090 static int stop_tracing; 1091 static void stop_hist(int sig) 1092 { 1093 stop_tracing = 1; 1094 } 1095 1096 /* 1097 * timerlat_hist_set_signals - handles the signal to stop the tool 1098 */ 1099 static void 1100 timerlat_hist_set_signals(struct timerlat_hist_params *params) 1101 { 1102 signal(SIGINT, stop_hist); 1103 if (params->duration) { 1104 signal(SIGALRM, stop_hist); 1105 alarm(params->duration); 1106 } 1107 } 1108 1109 int timerlat_hist_main(int argc, char *argv[]) 1110 { 1111 struct timerlat_hist_params *params; 1112 struct osnoise_tool *record = NULL; 1113 struct timerlat_u_params params_u; 1114 struct osnoise_tool *tool = NULL; 1115 struct osnoise_tool *aa = NULL; 1116 struct trace_instance *trace; 1117 int dma_latency_fd = -1; 1118 int return_value = 1; 1119 pthread_t timerlat_u; 1120 int retval; 1121 1122 params = timerlat_hist_parse_args(argc, argv); 1123 if (!params) 1124 exit(1); 1125 1126 tool = timerlat_init_hist(params); 1127 if (!tool) { 1128 err_msg("Could not init osnoise hist\n"); 1129 goto out_exit; 1130 } 1131 1132 retval = timerlat_hist_apply_config(tool, params); 1133 if (retval) { 1134 err_msg("Could not apply config\n"); 1135 goto out_free; 1136 } 1137 1138 trace = &tool->trace; 1139 1140 retval = enable_timerlat(trace); 1141 if (retval) { 1142 err_msg("Failed to enable timerlat tracer\n"); 1143 goto out_free; 1144 } 1145 1146 if (params->set_sched) { 1147 retval = set_comm_sched_attr("timerlat/", ¶ms->sched_param); 1148 if (retval) { 1149 err_msg("Failed to set sched parameters\n"); 1150 goto out_free; 1151 } 1152 } 1153 1154 if (params->cgroup && !params->user_workload) { 1155 retval = set_comm_cgroup("timerlat/", params->cgroup_name); 1156 if (!retval) { 1157 err_msg("Failed to move threads to cgroup\n"); 1158 goto out_free; 1159 } 1160 } 1161 1162 if (params->dma_latency >= 0) { 1163 dma_latency_fd = set_cpu_dma_latency(params->dma_latency); 1164 if (dma_latency_fd < 0) { 1165 err_msg("Could not set /dev/cpu_dma_latency.\n"); 1166 goto out_free; 1167 } 1168 } 1169 1170 if (params->trace_output) { 1171 record = osnoise_init_trace_tool("timerlat"); 1172 if (!record) { 1173 err_msg("Failed to enable the trace instance\n"); 1174 goto out_free; 1175 } 1176 1177 if (params->events) { 1178 retval = trace_events_enable(&record->trace, params->events); 1179 if (retval) 1180 goto out_hist; 1181 } 1182 } 1183 1184 if (!params->no_aa) { 1185 aa = osnoise_init_tool("timerlat_aa"); 1186 if (!aa) 1187 goto out_hist; 1188 1189 retval = timerlat_aa_init(aa, params->dump_tasks); 1190 if (retval) { 1191 err_msg("Failed to enable the auto analysis instance\n"); 1192 goto out_hist; 1193 } 1194 1195 retval = enable_timerlat(&aa->trace); 1196 if (retval) { 1197 err_msg("Failed to enable timerlat tracer\n"); 1198 goto out_hist; 1199 } 1200 } 1201 1202 if (params->user_workload) { 1203 /* rtla asked to stop */ 1204 params_u.should_run = 1; 1205 /* all threads left */ 1206 params_u.stopped_running = 0; 1207 1208 params_u.set = ¶ms->monitored_cpus; 1209 if (params->set_sched) 1210 params_u.sched_param = ¶ms->sched_param; 1211 else 1212 params_u.sched_param = NULL; 1213 1214 params_u.cgroup_name = params->cgroup_name; 1215 1216 retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, ¶ms_u); 1217 if (retval) 1218 err_msg("Error creating timerlat user-space threads\n"); 1219 } 1220 1221 if (params->warmup > 0) { 1222 debug_msg("Warming up for %d seconds\n", params->warmup); 1223 sleep(params->warmup); 1224 if (stop_tracing) 1225 goto out_hist; 1226 } 1227 1228 /* 1229 * Start the tracers here, after having set all instances. 1230 * 1231 * Let the trace instance start first for the case of hitting a stop 1232 * tracing while enabling other instances. The trace instance is the 1233 * one with most valuable information. 1234 */ 1235 if (params->trace_output) 1236 trace_instance_start(&record->trace); 1237 if (!params->no_aa) 1238 trace_instance_start(&aa->trace); 1239 trace_instance_start(trace); 1240 1241 tool->start_time = time(NULL); 1242 timerlat_hist_set_signals(params); 1243 1244 while (!stop_tracing) { 1245 sleep(params->sleep_time); 1246 1247 retval = tracefs_iterate_raw_events(trace->tep, 1248 trace->inst, 1249 NULL, 1250 0, 1251 collect_registered_events, 1252 trace); 1253 if (retval < 0) { 1254 err_msg("Error iterating on events\n"); 1255 goto out_hist; 1256 } 1257 1258 if (trace_is_off(&tool->trace, &record->trace)) 1259 break; 1260 1261 /* is there still any user-threads ? */ 1262 if (params->user_workload) { 1263 if (params_u.stopped_running) { 1264 debug_msg("timerlat user-space threads stopped!\n"); 1265 break; 1266 } 1267 } 1268 } 1269 1270 if (params->user_workload && !params_u.stopped_running) { 1271 params_u.should_run = 0; 1272 sleep(1); 1273 } 1274 1275 timerlat_print_stats(params, tool); 1276 1277 return_value = 0; 1278 1279 if (trace_is_off(&tool->trace, &record->trace)) { 1280 printf("rtla timerlat hit stop tracing\n"); 1281 1282 if (!params->no_aa) 1283 timerlat_auto_analysis(params->stop_us, params->stop_total_us); 1284 1285 if (params->trace_output) { 1286 printf(" Saving trace to %s\n", params->trace_output); 1287 save_trace_to_file(record->trace.inst, params->trace_output); 1288 } 1289 } 1290 1291 out_hist: 1292 timerlat_aa_destroy(); 1293 if (dma_latency_fd >= 0) 1294 close(dma_latency_fd); 1295 trace_events_destroy(&record->trace, params->events); 1296 params->events = NULL; 1297 out_free: 1298 timerlat_free_histogram(tool->data); 1299 osnoise_destroy_tool(aa); 1300 osnoise_destroy_tool(record); 1301 osnoise_destroy_tool(tool); 1302 free(params); 1303 out_exit: 1304 exit(return_value); 1305 } 1306