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