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 <errno.h> 13 #include <stdio.h> 14 #include <time.h> 15 16 #include "osnoise.h" 17 18 struct osnoise_hist_cpu { 19 int *samples; 20 int count; 21 22 unsigned long long min_sample; 23 unsigned long long sum_sample; 24 unsigned long long max_sample; 25 26 }; 27 28 struct osnoise_hist_data { 29 struct tracefs_hist *trace_hist; 30 struct osnoise_hist_cpu *hist; 31 int entries; 32 int bucket_size; 33 int nr_cpus; 34 }; 35 36 /* 37 * osnoise_free_histogram - free runtime data 38 */ 39 static void 40 osnoise_free_histogram(struct osnoise_hist_data *data) 41 { 42 int cpu; 43 44 /* one histogram for IRQ and one for thread, per CPU */ 45 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 46 if (data->hist[cpu].samples) 47 free(data->hist[cpu].samples); 48 } 49 50 /* one set of histograms per CPU */ 51 if (data->hist) 52 free(data->hist); 53 54 free(data); 55 } 56 57 /* 58 * osnoise_alloc_histogram - alloc runtime data 59 */ 60 static struct osnoise_hist_data 61 *osnoise_alloc_histogram(int nr_cpus, int entries, int bucket_size) 62 { 63 struct osnoise_hist_data *data; 64 int cpu; 65 66 data = calloc(1, sizeof(*data)); 67 if (!data) 68 return NULL; 69 70 data->entries = entries; 71 data->bucket_size = bucket_size; 72 data->nr_cpus = nr_cpus; 73 74 data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); 75 if (!data->hist) 76 goto cleanup; 77 78 for (cpu = 0; cpu < nr_cpus; cpu++) { 79 data->hist[cpu].samples = calloc(1, sizeof(*data->hist->samples) * (entries + 1)); 80 if (!data->hist[cpu].samples) 81 goto cleanup; 82 } 83 84 /* set the min to max */ 85 for (cpu = 0; cpu < nr_cpus; cpu++) 86 data->hist[cpu].min_sample = ~0; 87 88 return data; 89 90 cleanup: 91 osnoise_free_histogram(data); 92 return NULL; 93 } 94 95 static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu, 96 unsigned long long duration, int count) 97 { 98 struct osnoise_params *params = tool->params; 99 struct osnoise_hist_data *data = tool->data; 100 unsigned long long total_duration; 101 int entries = data->entries; 102 int bucket; 103 int *hist; 104 105 if (params->output_divisor) 106 duration = duration / params->output_divisor; 107 108 bucket = duration / data->bucket_size; 109 110 total_duration = duration * count; 111 112 hist = data->hist[cpu].samples; 113 data->hist[cpu].count += count; 114 update_min(&data->hist[cpu].min_sample, &duration); 115 update_sum(&data->hist[cpu].sum_sample, &total_duration); 116 update_max(&data->hist[cpu].max_sample, &duration); 117 118 if (bucket < entries) 119 hist[bucket] += count; 120 else 121 hist[entries] += count; 122 } 123 124 /* 125 * osnoise_destroy_trace_hist - disable events used to collect histogram 126 */ 127 static void osnoise_destroy_trace_hist(struct osnoise_tool *tool) 128 { 129 struct osnoise_hist_data *data = tool->data; 130 131 tracefs_hist_pause(tool->trace.inst, data->trace_hist); 132 tracefs_hist_destroy(tool->trace.inst, data->trace_hist); 133 } 134 135 /* 136 * osnoise_init_trace_hist - enable events used to collect histogram 137 */ 138 static int osnoise_init_trace_hist(struct osnoise_tool *tool) 139 { 140 struct osnoise_params *params = tool->params; 141 struct osnoise_hist_data *data = tool->data; 142 int bucket_size; 143 char buff[128]; 144 int retval = 0; 145 146 /* 147 * Set the size of the bucket. 148 */ 149 bucket_size = params->output_divisor * params->bucket_size; 150 snprintf(buff, sizeof(buff), "duration.buckets=%d", bucket_size); 151 152 data->trace_hist = tracefs_hist_alloc(tool->trace.tep, "osnoise", "sample_threshold", 153 buff, TRACEFS_HIST_KEY_NORMAL); 154 if (!data->trace_hist) 155 return 1; 156 157 retval = tracefs_hist_add_key(data->trace_hist, "cpu", 0); 158 if (retval) 159 goto out_err; 160 161 retval = tracefs_hist_start(tool->trace.inst, data->trace_hist); 162 if (retval) 163 goto out_err; 164 165 return 0; 166 167 out_err: 168 osnoise_destroy_trace_hist(tool); 169 return 1; 170 } 171 172 /* 173 * osnoise_read_trace_hist - parse histogram file and file osnoise histogram 174 */ 175 static void osnoise_read_trace_hist(struct osnoise_tool *tool) 176 { 177 struct osnoise_hist_data *data = tool->data; 178 long long cpu, counter, duration; 179 char *content, *position; 180 181 tracefs_hist_pause(tool->trace.inst, data->trace_hist); 182 183 content = tracefs_event_file_read(tool->trace.inst, "osnoise", 184 "sample_threshold", 185 "hist", NULL); 186 if (!content) 187 return; 188 189 position = content; 190 while (true) { 191 position = strstr(position, "duration: ~"); 192 if (!position) 193 break; 194 position += strlen("duration: ~"); 195 duration = get_llong_from_str(position); 196 if (duration == -1) 197 err_msg("error reading duration from histogram\n"); 198 199 position = strstr(position, "cpu:"); 200 if (!position) 201 break; 202 position += strlen("cpu: "); 203 cpu = get_llong_from_str(position); 204 if (cpu == -1) 205 err_msg("error reading cpu from histogram\n"); 206 207 position = strstr(position, "hitcount:"); 208 if (!position) 209 break; 210 position += strlen("hitcount: "); 211 counter = get_llong_from_str(position); 212 if (counter == -1) 213 err_msg("error reading counter from histogram\n"); 214 215 osnoise_hist_update_multiple(tool, cpu, duration, counter); 216 } 217 free(content); 218 } 219 220 /* 221 * osnoise_hist_header - print the header of the tracer to the output 222 */ 223 static void osnoise_hist_header(struct osnoise_tool *tool) 224 { 225 struct osnoise_params *params = tool->params; 226 struct osnoise_hist_data *data = tool->data; 227 struct trace_seq *s = tool->trace.seq; 228 char duration[26]; 229 int cpu; 230 231 if (params->no_header) 232 return; 233 234 get_duration(tool->start_time, duration, sizeof(duration)); 235 trace_seq_printf(s, "# RTLA osnoise histogram\n"); 236 trace_seq_printf(s, "# Time unit is %s (%s)\n", 237 params->output_divisor == 1 ? "nanoseconds" : "microseconds", 238 params->output_divisor == 1 ? "ns" : "us"); 239 240 trace_seq_printf(s, "# Duration: %s\n", duration); 241 242 if (!params->no_index) 243 trace_seq_printf(s, "Index"); 244 245 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 246 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 247 continue; 248 249 if (!data->hist[cpu].count) 250 continue; 251 252 trace_seq_printf(s, " CPU-%03d", cpu); 253 } 254 trace_seq_printf(s, "\n"); 255 256 trace_seq_do_printf(s); 257 trace_seq_reset(s); 258 } 259 260 /* 261 * osnoise_print_summary - print the summary of the hist data to the output 262 */ 263 static void 264 osnoise_print_summary(struct osnoise_params *params, 265 struct trace_instance *trace, 266 struct osnoise_hist_data *data) 267 { 268 int cpu; 269 270 if (params->no_summary) 271 return; 272 273 if (!params->no_index) 274 trace_seq_printf(trace->seq, "count:"); 275 276 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 277 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 278 continue; 279 280 if (!data->hist[cpu].count) 281 continue; 282 283 trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].count); 284 } 285 trace_seq_printf(trace->seq, "\n"); 286 287 if (!params->no_index) 288 trace_seq_printf(trace->seq, "min: "); 289 290 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 291 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 292 continue; 293 294 if (!data->hist[cpu].count) 295 continue; 296 297 trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].min_sample); 298 299 } 300 trace_seq_printf(trace->seq, "\n"); 301 302 if (!params->no_index) 303 trace_seq_printf(trace->seq, "avg: "); 304 305 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 306 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 307 continue; 308 309 if (!data->hist[cpu].count) 310 continue; 311 312 if (data->hist[cpu].count) 313 trace_seq_printf(trace->seq, "%9.2f ", 314 ((double) data->hist[cpu].sum_sample) / data->hist[cpu].count); 315 else 316 trace_seq_printf(trace->seq, " - "); 317 } 318 trace_seq_printf(trace->seq, "\n"); 319 320 if (!params->no_index) 321 trace_seq_printf(trace->seq, "max: "); 322 323 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 324 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 325 continue; 326 327 if (!data->hist[cpu].count) 328 continue; 329 330 trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].max_sample); 331 332 } 333 trace_seq_printf(trace->seq, "\n"); 334 trace_seq_do_printf(trace->seq); 335 trace_seq_reset(trace->seq); 336 } 337 338 /* 339 * osnoise_print_stats - print data for all CPUs 340 */ 341 static void 342 osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool) 343 { 344 struct osnoise_hist_data *data = tool->data; 345 struct trace_instance *trace = &tool->trace; 346 int has_samples = 0; 347 int bucket, cpu; 348 int total; 349 350 osnoise_hist_header(tool); 351 352 for (bucket = 0; bucket < data->entries; bucket++) { 353 total = 0; 354 355 if (!params->no_index) 356 trace_seq_printf(trace->seq, "%-6d", 357 bucket * data->bucket_size); 358 359 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 360 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 361 continue; 362 363 if (!data->hist[cpu].count) 364 continue; 365 366 total += data->hist[cpu].samples[bucket]; 367 trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].samples[bucket]); 368 } 369 370 if (total == 0 && !params->with_zeros) { 371 trace_seq_reset(trace->seq); 372 continue; 373 } 374 375 /* There are samples above the threshold */ 376 has_samples = 1; 377 trace_seq_printf(trace->seq, "\n"); 378 trace_seq_do_printf(trace->seq); 379 trace_seq_reset(trace->seq); 380 } 381 382 /* 383 * If no samples were recorded, skip calculations, print zeroed statistics 384 * and return. 385 */ 386 if (!has_samples) { 387 trace_seq_reset(trace->seq); 388 trace_seq_printf(trace->seq, "over: 0\ncount: 0\nmin: 0\navg: 0\nmax: 0\n"); 389 trace_seq_do_printf(trace->seq); 390 trace_seq_reset(trace->seq); 391 return; 392 } 393 394 if (!params->no_index) 395 trace_seq_printf(trace->seq, "over: "); 396 397 for (cpu = 0; cpu < data->nr_cpus; cpu++) { 398 if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) 399 continue; 400 401 if (!data->hist[cpu].count) 402 continue; 403 404 trace_seq_printf(trace->seq, "%9d ", 405 data->hist[cpu].samples[data->entries]); 406 } 407 trace_seq_printf(trace->seq, "\n"); 408 trace_seq_do_printf(trace->seq); 409 trace_seq_reset(trace->seq); 410 411 osnoise_print_summary(params, trace, data); 412 osnoise_report_missed_events(tool); 413 } 414 415 /* 416 * osnoise_hist_usage - prints osnoise hist usage message 417 */ 418 static void osnoise_hist_usage(char *usage) 419 { 420 int i; 421 422 static const char * const msg[] = { 423 "", 424 " usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 425 " [-T us] [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", 426 " [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\", 427 " [--no-index] [--with-zeros] [-C[=cgroup_name]] [--warm-up]", 428 "", 429 " -h/--help: print this menu", 430 " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", 431 " -p/--period us: osnoise period in us", 432 " -r/--runtime us: osnoise runtime in us", 433 " -s/--stop us: stop trace if a single sample is higher than the argument in us", 434 " -S/--stop-total us: stop trace if the total sample is higher than the argument in us", 435 " -T/--threshold us: the minimum delta to be considered a noise", 436 " -c/--cpus cpu-list: list of cpus to run osnoise threads", 437 " -H/--house-keeping cpus: run rtla control threads only on the given cpus", 438 " -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", 439 " -d/--duration time[s|m|h|d]: duration of the session", 440 " -D/--debug: print debug info", 441 " -t/--trace[file]: save the stopped trace to [file|osnoise_trace.txt]", 442 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", 443 " --filter <filter>: enable a trace event filter to the previous -e event", 444 " --trigger <trigger>: enable a trace event trigger to the previous -e event", 445 " -b/--bucket-size N: set the histogram bucket size (default 1)", 446 " -E/--entries N: set the number of entries of the histogram (default 256)", 447 " --no-header: do not print header", 448 " --no-summary: do not print summary", 449 " --no-index: do not print index", 450 " --with-zeros: print zero only entries", 451 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters", 452 " o:prio - use SCHED_OTHER with prio", 453 " r:prio - use SCHED_RR with prio", 454 " f:prio - use SCHED_FIFO with prio", 455 " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", 456 " in nanoseconds", 457 " --warm-up: let the workload run for s seconds before collecting data", 458 " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", 459 NULL, 460 }; 461 462 if (usage) 463 fprintf(stderr, "%s\n", usage); 464 465 fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n", 466 VERSION); 467 468 for (i = 0; msg[i]; i++) 469 fprintf(stderr, "%s\n", msg[i]); 470 471 if (usage) 472 exit(EXIT_FAILURE); 473 474 exit(EXIT_SUCCESS); 475 } 476 477 /* 478 * osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters 479 */ 480 static struct osnoise_params 481 *osnoise_hist_parse_args(int argc, char *argv[]) 482 { 483 struct osnoise_params *params; 484 struct trace_events *tevent; 485 int retval; 486 int c; 487 488 params = calloc(1, sizeof(*params)); 489 if (!params) 490 exit(1); 491 492 /* display data in microseconds */ 493 params->output_divisor = 1000; 494 params->bucket_size = 1; 495 params->entries = 256; 496 497 while (1) { 498 static struct option long_options[] = { 499 {"auto", required_argument, 0, 'a'}, 500 {"bucket-size", required_argument, 0, 'b'}, 501 {"entries", required_argument, 0, 'E'}, 502 {"cpus", required_argument, 0, 'c'}, 503 {"cgroup", optional_argument, 0, 'C'}, 504 {"debug", no_argument, 0, 'D'}, 505 {"duration", required_argument, 0, 'd'}, 506 {"house-keeping", required_argument, 0, 'H'}, 507 {"help", no_argument, 0, 'h'}, 508 {"period", required_argument, 0, 'p'}, 509 {"priority", required_argument, 0, 'P'}, 510 {"runtime", required_argument, 0, 'r'}, 511 {"stop", required_argument, 0, 's'}, 512 {"stop-total", required_argument, 0, 'S'}, 513 {"trace", optional_argument, 0, 't'}, 514 {"event", required_argument, 0, 'e'}, 515 {"threshold", required_argument, 0, 'T'}, 516 {"no-header", no_argument, 0, '0'}, 517 {"no-summary", no_argument, 0, '1'}, 518 {"no-index", no_argument, 0, '2'}, 519 {"with-zeros", no_argument, 0, '3'}, 520 {"trigger", required_argument, 0, '4'}, 521 {"filter", required_argument, 0, '5'}, 522 {"warm-up", required_argument, 0, '6'}, 523 {"trace-buffer-size", required_argument, 0, '7'}, 524 {0, 0, 0, 0} 525 }; 526 527 /* getopt_long stores the option index here. */ 528 int option_index = 0; 529 530 c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:", 531 long_options, &option_index); 532 533 /* detect the end of the options. */ 534 if (c == -1) 535 break; 536 537 switch (c) { 538 case 'a': 539 /* set sample stop to auto_thresh */ 540 params->stop_us = get_llong_from_str(optarg); 541 542 /* set sample threshold to 1 */ 543 params->threshold = 1; 544 545 /* set trace */ 546 params->trace_output = "osnoise_trace.txt"; 547 548 break; 549 case 'b': 550 params->bucket_size = get_llong_from_str(optarg); 551 if ((params->bucket_size == 0) || (params->bucket_size >= 1000000)) 552 osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); 553 break; 554 case 'c': 555 retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); 556 if (retval) 557 osnoise_hist_usage("\nInvalid -c cpu list\n"); 558 params->cpus = optarg; 559 break; 560 case 'C': 561 params->cgroup = 1; 562 if (!optarg) { 563 /* will inherit this cgroup */ 564 params->cgroup_name = NULL; 565 } else if (*optarg == '=') { 566 /* skip the = */ 567 params->cgroup_name = ++optarg; 568 } 569 break; 570 case 'D': 571 config_debug = 1; 572 break; 573 case 'd': 574 params->duration = parse_seconds_duration(optarg); 575 if (!params->duration) 576 osnoise_hist_usage("Invalid -D duration\n"); 577 break; 578 case 'e': 579 tevent = trace_event_alloc(optarg); 580 if (!tevent) { 581 err_msg("Error alloc trace event"); 582 exit(EXIT_FAILURE); 583 } 584 585 if (params->events) 586 tevent->next = params->events; 587 588 params->events = tevent; 589 break; 590 case 'E': 591 params->entries = get_llong_from_str(optarg); 592 if ((params->entries < 10) || (params->entries > 9999999)) 593 osnoise_hist_usage("Entries must be > 10 and < 9999999\n"); 594 break; 595 case 'h': 596 case '?': 597 osnoise_hist_usage(NULL); 598 break; 599 case 'H': 600 params->hk_cpus = 1; 601 retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); 602 if (retval) { 603 err_msg("Error parsing house keeping CPUs\n"); 604 exit(EXIT_FAILURE); 605 } 606 break; 607 case 'p': 608 params->period = get_llong_from_str(optarg); 609 if (params->period > 10000000) 610 osnoise_hist_usage("Period longer than 10 s\n"); 611 break; 612 case 'P': 613 retval = parse_prio(optarg, ¶ms->sched_param); 614 if (retval == -1) 615 osnoise_hist_usage("Invalid -P priority"); 616 params->set_sched = 1; 617 break; 618 case 'r': 619 params->runtime = get_llong_from_str(optarg); 620 if (params->runtime < 100) 621 osnoise_hist_usage("Runtime shorter than 100 us\n"); 622 break; 623 case 's': 624 params->stop_us = get_llong_from_str(optarg); 625 break; 626 case 'S': 627 params->stop_total_us = get_llong_from_str(optarg); 628 break; 629 case 'T': 630 params->threshold = get_llong_from_str(optarg); 631 break; 632 case 't': 633 if (optarg) { 634 if (optarg[0] == '=') 635 params->trace_output = &optarg[1]; 636 else 637 params->trace_output = &optarg[0]; 638 } else if (optind < argc && argv[optind][0] != '0') 639 params->trace_output = argv[optind]; 640 else 641 params->trace_output = "osnoise_trace.txt"; 642 break; 643 case '0': /* no header */ 644 params->no_header = 1; 645 break; 646 case '1': /* no summary */ 647 params->no_summary = 1; 648 break; 649 case '2': /* no index */ 650 params->no_index = 1; 651 break; 652 case '3': /* with zeros */ 653 params->with_zeros = 1; 654 break; 655 case '4': /* trigger */ 656 if (params->events) { 657 retval = trace_event_add_trigger(params->events, optarg); 658 if (retval) { 659 err_msg("Error adding trigger %s\n", optarg); 660 exit(EXIT_FAILURE); 661 } 662 } else { 663 osnoise_hist_usage("--trigger requires a previous -e\n"); 664 } 665 break; 666 case '5': /* filter */ 667 if (params->events) { 668 retval = trace_event_add_filter(params->events, optarg); 669 if (retval) { 670 err_msg("Error adding filter %s\n", optarg); 671 exit(EXIT_FAILURE); 672 } 673 } else { 674 osnoise_hist_usage("--filter requires a previous -e\n"); 675 } 676 break; 677 case '6': 678 params->warmup = get_llong_from_str(optarg); 679 break; 680 case '7': 681 params->buffer_size = get_llong_from_str(optarg); 682 break; 683 default: 684 osnoise_hist_usage("Invalid option"); 685 } 686 } 687 688 if (geteuid()) { 689 err_msg("rtla needs root permission\n"); 690 exit(EXIT_FAILURE); 691 } 692 693 if (params->no_index && !params->with_zeros) 694 osnoise_hist_usage("no-index set and with-zeros not set - it does not make sense"); 695 696 return params; 697 } 698 699 /* 700 * osnoise_hist_apply_config - apply the hist configs to the initialized tool 701 */ 702 static int 703 osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) 704 { 705 int retval; 706 707 retval = osnoise_apply_config(tool, params); 708 if (retval) 709 goto out_err; 710 711 return 0; 712 713 out_err: 714 return -1; 715 } 716 717 /* 718 * osnoise_init_hist - initialize a osnoise hist tool with parameters 719 */ 720 static struct osnoise_tool 721 *osnoise_init_hist(struct osnoise_params *params) 722 { 723 struct osnoise_tool *tool; 724 int nr_cpus; 725 726 nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 727 728 tool = osnoise_init_tool("osnoise_hist"); 729 if (!tool) 730 return NULL; 731 732 tool->data = osnoise_alloc_histogram(nr_cpus, params->entries, params->bucket_size); 733 if (!tool->data) 734 goto out_err; 735 736 tool->params = params; 737 738 return tool; 739 740 out_err: 741 osnoise_destroy_tool(tool); 742 return NULL; 743 } 744 745 static int stop_tracing; 746 static void stop_hist(int sig) 747 { 748 stop_tracing = 1; 749 } 750 751 /* 752 * osnoise_hist_set_signals - handles the signal to stop the tool 753 */ 754 static void 755 osnoise_hist_set_signals(struct osnoise_params *params) 756 { 757 signal(SIGINT, stop_hist); 758 if (params->duration) { 759 signal(SIGALRM, stop_hist); 760 alarm(params->duration); 761 } 762 } 763 764 int osnoise_hist_main(int argc, char *argv[]) 765 { 766 struct osnoise_params *params; 767 struct osnoise_tool *record = NULL; 768 struct osnoise_tool *tool = NULL; 769 enum result return_value = ERROR; 770 struct trace_instance *trace; 771 int retval; 772 773 params = osnoise_hist_parse_args(argc, argv); 774 if (!params) 775 exit(1); 776 777 tool = osnoise_init_hist(params); 778 if (!tool) { 779 err_msg("Could not init osnoise hist\n"); 780 goto out_exit; 781 } 782 783 retval = osnoise_hist_apply_config(tool, params); 784 if (retval) { 785 err_msg("Could not apply config\n"); 786 goto out_destroy; 787 } 788 789 trace = &tool->trace; 790 791 retval = enable_osnoise(trace); 792 if (retval) { 793 err_msg("Failed to enable osnoise tracer\n"); 794 goto out_destroy; 795 } 796 797 retval = osnoise_init_trace_hist(tool); 798 if (retval) 799 goto out_destroy; 800 801 if (params->set_sched) { 802 retval = set_comm_sched_attr("osnoise/", ¶ms->sched_param); 803 if (retval) { 804 err_msg("Failed to set sched parameters\n"); 805 goto out_free; 806 } 807 } 808 809 if (params->cgroup) { 810 retval = set_comm_cgroup("timerlat/", params->cgroup_name); 811 if (!retval) { 812 err_msg("Failed to move threads to cgroup\n"); 813 goto out_free; 814 } 815 } 816 817 if (params->trace_output) { 818 record = osnoise_init_trace_tool("osnoise"); 819 if (!record) { 820 err_msg("Failed to enable the trace instance\n"); 821 goto out_free; 822 } 823 824 if (params->events) { 825 retval = trace_events_enable(&record->trace, params->events); 826 if (retval) 827 goto out_hist; 828 } 829 830 if (params->buffer_size > 0) { 831 retval = trace_set_buffer_size(&record->trace, params->buffer_size); 832 if (retval) 833 goto out_hist; 834 } 835 } 836 837 /* 838 * Start the tracer here, after having set all instances. 839 * 840 * Let the trace instance start first for the case of hitting a stop 841 * tracing while enabling other instances. The trace instance is the 842 * one with most valuable information. 843 */ 844 if (params->trace_output) 845 trace_instance_start(&record->trace); 846 trace_instance_start(trace); 847 848 if (params->warmup > 0) { 849 debug_msg("Warming up for %d seconds\n", params->warmup); 850 sleep(params->warmup); 851 if (stop_tracing) 852 goto out_hist; 853 854 /* 855 * Clean up the buffer. The osnoise workload do not run 856 * with tracing off to avoid creating a performance penalty 857 * when not needed. 858 */ 859 retval = tracefs_instance_file_write(trace->inst, "trace", ""); 860 if (retval < 0) { 861 debug_msg("Error cleaning up the buffer"); 862 goto out_hist; 863 } 864 865 } 866 867 tool->start_time = time(NULL); 868 osnoise_hist_set_signals(params); 869 870 while (!stop_tracing) { 871 sleep(params->sleep_time); 872 873 retval = tracefs_iterate_raw_events(trace->tep, 874 trace->inst, 875 NULL, 876 0, 877 collect_registered_events, 878 trace); 879 if (retval < 0) { 880 err_msg("Error iterating on events\n"); 881 goto out_hist; 882 } 883 884 if (osnoise_trace_is_off(tool, record)) 885 break; 886 } 887 888 osnoise_read_trace_hist(tool); 889 890 osnoise_print_stats(params, tool); 891 892 return_value = PASSED; 893 894 if (osnoise_trace_is_off(tool, record)) { 895 printf("rtla osnoise hit stop tracing\n"); 896 save_trace_to_file(record ? record->trace.inst : NULL, 897 params->trace_output); 898 return_value = FAILED; 899 } 900 901 out_hist: 902 trace_events_destroy(&record->trace, params->events); 903 params->events = NULL; 904 out_free: 905 osnoise_free_histogram(tool->data); 906 out_destroy: 907 osnoise_destroy_tool(record); 908 osnoise_destroy_tool(tool); 909 free(params); 910 out_exit: 911 exit(return_value); 912 } 913