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