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