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