Commit | Line | Data |
---|---|---|
1eceb2fc DBO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> | |
4 | */ | |
5 | ||
272ced25 | 6 | #define _GNU_SOURCE |
1eceb2fc DBO |
7 | #include <getopt.h> |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <signal.h> | |
11 | #include <unistd.h> | |
12 | #include <stdio.h> | |
13 | #include <time.h> | |
272ced25 | 14 | #include <sched.h> |
1eceb2fc DBO |
15 | |
16 | #include "osnoise.h" | |
17 | #include "utils.h" | |
18 | ||
1f428356 DBO |
19 | enum osnoise_mode { |
20 | MODE_OSNOISE = 0, | |
21 | MODE_HWNOISE | |
22 | }; | |
23 | ||
1eceb2fc DBO |
24 | /* |
25 | * osnoise top parameters | |
26 | */ | |
27 | struct osnoise_top_params { | |
28 | char *cpus; | |
894c29c7 | 29 | cpu_set_t monitored_cpus; |
1eceb2fc | 30 | char *trace_output; |
a957cbc0 | 31 | char *cgroup_name; |
1eceb2fc DBO |
32 | unsigned long long runtime; |
33 | unsigned long long period; | |
d635316a | 34 | long long threshold; |
1eceb2fc DBO |
35 | long long stop_us; |
36 | long long stop_total_us; | |
37 | int sleep_time; | |
38 | int duration; | |
39 | int quiet; | |
40 | int set_sched; | |
a957cbc0 | 41 | int cgroup; |
272ced25 | 42 | int hk_cpus; |
cdbf7196 | 43 | int warmup; |
e9a4062e | 44 | int buffer_size; |
272ced25 | 45 | cpu_set_t hk_cpu_set; |
1eceb2fc | 46 | struct sched_attr sched_param; |
51d64c3a | 47 | struct trace_events *events; |
1f428356 | 48 | enum osnoise_mode mode; |
1eceb2fc DBO |
49 | }; |
50 | ||
51 | struct osnoise_top_cpu { | |
52 | unsigned long long sum_runtime; | |
53 | unsigned long long sum_noise; | |
54 | unsigned long long max_noise; | |
55 | unsigned long long max_sample; | |
56 | ||
57 | unsigned long long hw_count; | |
58 | unsigned long long nmi_count; | |
59 | unsigned long long irq_count; | |
60 | unsigned long long softirq_count; | |
61 | unsigned long long thread_count; | |
62 | ||
63 | int sum_cycles; | |
64 | }; | |
65 | ||
66 | struct osnoise_top_data { | |
67 | struct osnoise_top_cpu *cpu_data; | |
68 | int nr_cpus; | |
69 | }; | |
70 | ||
71 | /* | |
72 | * osnoise_free_top - free runtime data | |
73 | */ | |
74 | static void | |
75 | osnoise_free_top(struct osnoise_top_data *data) | |
76 | { | |
77 | free(data->cpu_data); | |
78 | free(data); | |
79 | } | |
80 | ||
81 | /* | |
82 | * osnoise_alloc_histogram - alloc runtime data | |
83 | */ | |
84 | static struct osnoise_top_data *osnoise_alloc_top(int nr_cpus) | |
85 | { | |
86 | struct osnoise_top_data *data; | |
87 | ||
88 | data = calloc(1, sizeof(*data)); | |
89 | if (!data) | |
90 | return NULL; | |
91 | ||
92 | data->nr_cpus = nr_cpus; | |
93 | ||
94 | /* one set of histograms per CPU */ | |
95 | data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus); | |
96 | if (!data->cpu_data) | |
97 | goto cleanup; | |
98 | ||
99 | return data; | |
100 | ||
101 | cleanup: | |
102 | osnoise_free_top(data); | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | /* | |
107 | * osnoise_top_handler - this is the handler for osnoise tracer events | |
108 | */ | |
109 | static int | |
110 | osnoise_top_handler(struct trace_seq *s, struct tep_record *record, | |
111 | struct tep_event *event, void *context) | |
112 | { | |
113 | struct trace_instance *trace = context; | |
114 | struct osnoise_tool *tool; | |
115 | unsigned long long val; | |
116 | struct osnoise_top_cpu *cpu_data; | |
117 | struct osnoise_top_data *data; | |
118 | int cpu = record->cpu; | |
119 | ||
120 | tool = container_of(trace, struct osnoise_tool, trace); | |
121 | ||
122 | data = tool->data; | |
123 | cpu_data = &data->cpu_data[cpu]; | |
124 | ||
125 | cpu_data->sum_cycles++; | |
126 | ||
127 | tep_get_field_val(s, event, "runtime", record, &val, 1); | |
128 | update_sum(&cpu_data->sum_runtime, &val); | |
129 | ||
130 | tep_get_field_val(s, event, "noise", record, &val, 1); | |
131 | update_max(&cpu_data->max_noise, &val); | |
132 | update_sum(&cpu_data->sum_noise, &val); | |
133 | ||
134 | tep_get_field_val(s, event, "max_sample", record, &val, 1); | |
135 | update_max(&cpu_data->max_sample, &val); | |
136 | ||
137 | tep_get_field_val(s, event, "hw_count", record, &val, 1); | |
138 | update_sum(&cpu_data->hw_count, &val); | |
139 | ||
140 | tep_get_field_val(s, event, "nmi_count", record, &val, 1); | |
141 | update_sum(&cpu_data->nmi_count, &val); | |
142 | ||
143 | tep_get_field_val(s, event, "irq_count", record, &val, 1); | |
144 | update_sum(&cpu_data->irq_count, &val); | |
145 | ||
146 | tep_get_field_val(s, event, "softirq_count", record, &val, 1); | |
147 | update_sum(&cpu_data->softirq_count, &val); | |
148 | ||
149 | tep_get_field_val(s, event, "thread_count", record, &val, 1); | |
150 | update_sum(&cpu_data->thread_count, &val); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | /* | |
156 | * osnoise_top_header - print the header of the tool output | |
157 | */ | |
158 | static void osnoise_top_header(struct osnoise_tool *top) | |
159 | { | |
1f428356 | 160 | struct osnoise_top_params *params = top->params; |
1eceb2fc DBO |
161 | struct trace_seq *s = top->trace.seq; |
162 | char duration[26]; | |
163 | ||
164 | get_duration(top->start_time, duration, sizeof(duration)); | |
165 | ||
166 | trace_seq_printf(s, "\033[2;37;40m"); | |
1f428356 DBO |
167 | trace_seq_printf(s, " "); |
168 | ||
169 | if (params->mode == MODE_OSNOISE) { | |
170 | trace_seq_printf(s, "Operating System Noise"); | |
171 | trace_seq_printf(s, " "); | |
172 | } else if (params->mode == MODE_HWNOISE) { | |
173 | trace_seq_printf(s, "Hardware-related Noise"); | |
174 | } | |
175 | ||
176 | trace_seq_printf(s, " "); | |
1eceb2fc DBO |
177 | trace_seq_printf(s, "\033[0;0;0m"); |
178 | trace_seq_printf(s, "\n"); | |
179 | ||
180 | trace_seq_printf(s, "duration: %9s | time is in us\n", duration); | |
181 | ||
182 | trace_seq_printf(s, "\033[2;30;47m"); | |
183 | trace_seq_printf(s, "CPU Period Runtime "); | |
184 | trace_seq_printf(s, " Noise "); | |
185 | trace_seq_printf(s, " %% CPU Aval "); | |
186 | trace_seq_printf(s, " Max Noise Max Single "); | |
1f428356 DBO |
187 | trace_seq_printf(s, " HW NMI"); |
188 | ||
189 | if (params->mode == MODE_HWNOISE) | |
190 | goto eol; | |
191 | ||
192 | trace_seq_printf(s, " IRQ Softirq Thread"); | |
193 | ||
194 | eol: | |
1eceb2fc DBO |
195 | trace_seq_printf(s, "\033[0;0;0m"); |
196 | trace_seq_printf(s, "\n"); | |
197 | } | |
198 | ||
199 | /* | |
200 | * clear_terminal - clears the output terminal | |
201 | */ | |
202 | static void clear_terminal(struct trace_seq *seq) | |
203 | { | |
204 | if (!config_debug) | |
205 | trace_seq_printf(seq, "\033c"); | |
206 | } | |
207 | ||
208 | /* | |
209 | * osnoise_top_print - prints the output of a given CPU | |
210 | */ | |
211 | static void osnoise_top_print(struct osnoise_tool *tool, int cpu) | |
212 | { | |
1f428356 | 213 | struct osnoise_top_params *params = tool->params; |
1eceb2fc DBO |
214 | struct trace_seq *s = tool->trace.seq; |
215 | struct osnoise_top_cpu *cpu_data; | |
216 | struct osnoise_top_data *data; | |
217 | int percentage; | |
218 | int decimal; | |
219 | ||
220 | data = tool->data; | |
221 | cpu_data = &data->cpu_data[cpu]; | |
222 | ||
223 | if (!cpu_data->sum_runtime) | |
224 | return; | |
225 | ||
226 | percentage = ((cpu_data->sum_runtime - cpu_data->sum_noise) * 10000000) | |
227 | / cpu_data->sum_runtime; | |
228 | decimal = percentage % 100000; | |
229 | percentage = percentage / 100000; | |
230 | ||
231 | trace_seq_printf(s, "%3d #%-6d %12llu ", cpu, cpu_data->sum_cycles, cpu_data->sum_runtime); | |
232 | trace_seq_printf(s, "%12llu ", cpu_data->sum_noise); | |
233 | trace_seq_printf(s, " %3d.%05d", percentage, decimal); | |
234 | trace_seq_printf(s, "%12llu %12llu", cpu_data->max_noise, cpu_data->max_sample); | |
235 | ||
236 | trace_seq_printf(s, "%12llu ", cpu_data->hw_count); | |
237 | trace_seq_printf(s, "%12llu ", cpu_data->nmi_count); | |
1f428356 DBO |
238 | |
239 | if (params->mode == MODE_HWNOISE) { | |
240 | trace_seq_printf(s, "\n"); | |
241 | return; | |
242 | } | |
243 | ||
1eceb2fc DBO |
244 | trace_seq_printf(s, "%12llu ", cpu_data->irq_count); |
245 | trace_seq_printf(s, "%12llu ", cpu_data->softirq_count); | |
246 | trace_seq_printf(s, "%12llu\n", cpu_data->thread_count); | |
247 | } | |
248 | ||
249 | /* | |
250 | * osnoise_print_stats - print data for all cpus | |
251 | */ | |
252 | static void | |
253 | osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *top) | |
254 | { | |
255 | struct trace_instance *trace = &top->trace; | |
256 | static int nr_cpus = -1; | |
257 | int i; | |
258 | ||
259 | if (nr_cpus == -1) | |
260 | nr_cpus = sysconf(_SC_NPROCESSORS_CONF); | |
261 | ||
262 | if (!params->quiet) | |
263 | clear_terminal(trace->seq); | |
264 | ||
265 | osnoise_top_header(top); | |
266 | ||
267 | for (i = 0; i < nr_cpus; i++) { | |
894c29c7 | 268 | if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) |
1eceb2fc DBO |
269 | continue; |
270 | osnoise_top_print(top, i); | |
271 | } | |
272 | ||
273 | trace_seq_do_printf(trace->seq); | |
274 | trace_seq_reset(trace->seq); | |
275 | } | |
276 | ||
277 | /* | |
278 | * osnoise_top_usage - prints osnoise top usage message | |
279 | */ | |
1f428356 | 280 | static void osnoise_top_usage(struct osnoise_top_params *params, char *usage) |
1eceb2fc DBO |
281 | { |
282 | int i; | |
283 | ||
284 | static const char * const msg[] = { | |
1f428356 | 285 | " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", |
44f3a37d | 286 | " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", |
cdbf7196 | 287 | " [-c cpu-list] [-H cpu-list] [-P priority] [-C[=cgroup_name]] [--warm-up s]", |
1eceb2fc DBO |
288 | "", |
289 | " -h/--help: print this menu", | |
2b622edd | 290 | " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", |
1eceb2fc DBO |
291 | " -p/--period us: osnoise period in us", |
292 | " -r/--runtime us: osnoise runtime in us", | |
293 | " -s/--stop us: stop trace if a single sample is higher than the argument in us", | |
294 | " -S/--stop-total us: stop trace if the total sample is higher than the argument in us", | |
d635316a | 295 | " -T/--threshold us: the minimum delta to be considered a noise", |
1eceb2fc | 296 | " -c/--cpus cpu-list: list of cpus to run osnoise threads", |
272ced25 | 297 | " -H/--house-keeping cpus: run rtla control threads only on the given cpus", |
a957cbc0 | 298 | " -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", |
1eceb2fc DBO |
299 | " -d/--duration time[s|m|h|d]: duration of the session", |
300 | " -D/--debug: print debug info", | |
301 | " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]", | |
51d64c3a | 302 | " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", |
44f3a37d | 303 | " --filter <filter>: enable a trace event filter to the previous -e event", |
1a754893 | 304 | " --trigger <trigger>: enable a trace event trigger to the previous -e event", |
1eceb2fc DBO |
305 | " -q/--quiet print only a summary at the end", |
306 | " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", | |
307 | " o:prio - use SCHED_OTHER with prio", | |
308 | " r:prio - use SCHED_RR with prio", | |
309 | " f:prio - use SCHED_FIFO with prio", | |
310 | " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", | |
311 | " in nanoseconds", | |
cdbf7196 | 312 | " --warm-up s: let the workload run for s seconds before collecting data", |
e9a4062e | 313 | " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", |
1eceb2fc DBO |
314 | NULL, |
315 | }; | |
316 | ||
317 | if (usage) | |
318 | fprintf(stderr, "%s\n", usage); | |
319 | ||
1f428356 DBO |
320 | if (params->mode == MODE_OSNOISE) { |
321 | fprintf(stderr, | |
322 | "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n", | |
1eceb2fc DBO |
323 | VERSION); |
324 | ||
1f428356 DBO |
325 | fprintf(stderr, " usage: rtla osnoise [top]"); |
326 | } | |
327 | ||
328 | if (params->mode == MODE_HWNOISE) { | |
329 | fprintf(stderr, | |
330 | "rtla hwnoise: a summary of hardware-related noise (version %s)\n", | |
331 | VERSION); | |
332 | ||
333 | fprintf(stderr, " usage: rtla hwnoise"); | |
334 | } | |
335 | ||
1eceb2fc DBO |
336 | for (i = 0; msg[i]; i++) |
337 | fprintf(stderr, "%s\n", msg[i]); | |
b5f31936 JK |
338 | |
339 | if (usage) | |
340 | exit(EXIT_FAILURE); | |
341 | ||
342 | exit(EXIT_SUCCESS); | |
1eceb2fc DBO |
343 | } |
344 | ||
345 | /* | |
346 | * osnoise_top_parse_args - allocs, parse and fill the cmd line parameters | |
347 | */ | |
348 | struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) | |
349 | { | |
350 | struct osnoise_top_params *params; | |
51d64c3a | 351 | struct trace_events *tevent; |
1eceb2fc DBO |
352 | int retval; |
353 | int c; | |
354 | ||
355 | params = calloc(1, sizeof(*params)); | |
356 | if (!params) | |
357 | exit(1); | |
358 | ||
7bc4d308 | 359 | if (strcmp(argv[0], "hwnoise") == 0) { |
1f428356 | 360 | params->mode = MODE_HWNOISE; |
7bc4d308 DBO |
361 | /* |
362 | * Reduce CPU usage for 75% to avoid killing the system. | |
363 | */ | |
364 | params->runtime = 750000; | |
365 | params->period = 1000000; | |
366 | } | |
1f428356 | 367 | |
1eceb2fc DBO |
368 | while (1) { |
369 | static struct option long_options[] = { | |
2b622edd | 370 | {"auto", required_argument, 0, 'a'}, |
1eceb2fc | 371 | {"cpus", required_argument, 0, 'c'}, |
a957cbc0 | 372 | {"cgroup", optional_argument, 0, 'C'}, |
1eceb2fc DBO |
373 | {"debug", no_argument, 0, 'D'}, |
374 | {"duration", required_argument, 0, 'd'}, | |
51d64c3a | 375 | {"event", required_argument, 0, 'e'}, |
272ced25 | 376 | {"house-keeping", required_argument, 0, 'H'}, |
1eceb2fc DBO |
377 | {"help", no_argument, 0, 'h'}, |
378 | {"period", required_argument, 0, 'p'}, | |
379 | {"priority", required_argument, 0, 'P'}, | |
380 | {"quiet", no_argument, 0, 'q'}, | |
381 | {"runtime", required_argument, 0, 'r'}, | |
382 | {"stop", required_argument, 0, 's'}, | |
383 | {"stop-total", required_argument, 0, 'S'}, | |
d635316a | 384 | {"threshold", required_argument, 0, 'T'}, |
1eceb2fc | 385 | {"trace", optional_argument, 0, 't'}, |
1a754893 | 386 | {"trigger", required_argument, 0, '0'}, |
44f3a37d | 387 | {"filter", required_argument, 0, '1'}, |
cdbf7196 | 388 | {"warm-up", required_argument, 0, '2'}, |
e9a4062e | 389 | {"trace-buffer-size", required_argument, 0, '3'}, |
1eceb2fc DBO |
390 | {0, 0, 0, 0} |
391 | }; | |
392 | ||
393 | /* getopt_long stores the option index here. */ | |
394 | int option_index = 0; | |
395 | ||
e9a4062e | 396 | c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:", |
1eceb2fc DBO |
397 | long_options, &option_index); |
398 | ||
399 | /* Detect the end of the options. */ | |
400 | if (c == -1) | |
401 | break; | |
402 | ||
403 | switch (c) { | |
2b622edd DBO |
404 | case 'a': |
405 | /* set sample stop to auto_thresh */ | |
406 | params->stop_us = get_llong_from_str(optarg); | |
407 | ||
408 | /* set sample threshold to 1 */ | |
409 | params->threshold = 1; | |
410 | ||
411 | /* set trace */ | |
412 | params->trace_output = "osnoise_trace.txt"; | |
413 | ||
414 | break; | |
1eceb2fc | 415 | case 'c': |
894c29c7 | 416 | retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); |
1eceb2fc | 417 | if (retval) |
1f428356 | 418 | osnoise_top_usage(params, "\nInvalid -c cpu list\n"); |
1eceb2fc DBO |
419 | params->cpus = optarg; |
420 | break; | |
a957cbc0 DBO |
421 | case 'C': |
422 | params->cgroup = 1; | |
423 | if (!optarg) { | |
424 | /* will inherit this cgroup */ | |
425 | params->cgroup_name = NULL; | |
426 | } else if (*optarg == '=') { | |
427 | /* skip the = */ | |
428 | params->cgroup_name = ++optarg; | |
429 | } | |
430 | break; | |
1eceb2fc DBO |
431 | case 'D': |
432 | config_debug = 1; | |
433 | break; | |
434 | case 'd': | |
435 | params->duration = parse_seconds_duration(optarg); | |
436 | if (!params->duration) | |
1f428356 | 437 | osnoise_top_usage(params, "Invalid -D duration\n"); |
1eceb2fc | 438 | break; |
51d64c3a DBO |
439 | case 'e': |
440 | tevent = trace_event_alloc(optarg); | |
441 | if (!tevent) { | |
442 | err_msg("Error alloc trace event"); | |
443 | exit(EXIT_FAILURE); | |
444 | } | |
445 | ||
446 | if (params->events) | |
447 | tevent->next = params->events; | |
448 | params->events = tevent; | |
449 | ||
450 | break; | |
1eceb2fc DBO |
451 | case 'h': |
452 | case '?': | |
1f428356 | 453 | osnoise_top_usage(params, NULL); |
1eceb2fc | 454 | break; |
272ced25 DBO |
455 | case 'H': |
456 | params->hk_cpus = 1; | |
457 | retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); | |
458 | if (retval) { | |
459 | err_msg("Error parsing house keeping CPUs\n"); | |
460 | exit(EXIT_FAILURE); | |
461 | } | |
462 | break; | |
1eceb2fc DBO |
463 | case 'p': |
464 | params->period = get_llong_from_str(optarg); | |
465 | if (params->period > 10000000) | |
1f428356 | 466 | osnoise_top_usage(params, "Period longer than 10 s\n"); |
1eceb2fc DBO |
467 | break; |
468 | case 'P': | |
469 | retval = parse_prio(optarg, ¶ms->sched_param); | |
470 | if (retval == -1) | |
1f428356 | 471 | osnoise_top_usage(params, "Invalid -P priority"); |
1eceb2fc DBO |
472 | params->set_sched = 1; |
473 | break; | |
474 | case 'q': | |
475 | params->quiet = 1; | |
476 | break; | |
477 | case 'r': | |
478 | params->runtime = get_llong_from_str(optarg); | |
479 | if (params->runtime < 100) | |
1f428356 | 480 | osnoise_top_usage(params, "Runtime shorter than 100 us\n"); |
1eceb2fc DBO |
481 | break; |
482 | case 's': | |
483 | params->stop_us = get_llong_from_str(optarg); | |
484 | break; | |
485 | case 'S': | |
486 | params->stop_total_us = get_llong_from_str(optarg); | |
487 | break; | |
488 | case 't': | |
489 | if (optarg) | |
490 | /* skip = */ | |
491 | params->trace_output = &optarg[1]; | |
492 | else | |
493 | params->trace_output = "osnoise_trace.txt"; | |
494 | break; | |
d635316a DBO |
495 | case 'T': |
496 | params->threshold = get_llong_from_str(optarg); | |
497 | break; | |
1a754893 DBO |
498 | case '0': /* trigger */ |
499 | if (params->events) { | |
500 | retval = trace_event_add_trigger(params->events, optarg); | |
501 | if (retval) { | |
502 | err_msg("Error adding trigger %s\n", optarg); | |
503 | exit(EXIT_FAILURE); | |
504 | } | |
505 | } else { | |
1f428356 | 506 | osnoise_top_usage(params, "--trigger requires a previous -e\n"); |
1a754893 DBO |
507 | } |
508 | break; | |
44f3a37d DBO |
509 | case '1': /* filter */ |
510 | if (params->events) { | |
511 | retval = trace_event_add_filter(params->events, optarg); | |
512 | if (retval) { | |
513 | err_msg("Error adding filter %s\n", optarg); | |
514 | exit(EXIT_FAILURE); | |
515 | } | |
516 | } else { | |
1f428356 | 517 | osnoise_top_usage(params, "--filter requires a previous -e\n"); |
44f3a37d DBO |
518 | } |
519 | break; | |
cdbf7196 DBO |
520 | case '2': |
521 | params->warmup = get_llong_from_str(optarg); | |
522 | break; | |
e9a4062e DBO |
523 | case '3': |
524 | params->buffer_size = get_llong_from_str(optarg); | |
525 | break; | |
1eceb2fc | 526 | default: |
1f428356 | 527 | osnoise_top_usage(params, "Invalid option"); |
1eceb2fc DBO |
528 | } |
529 | } | |
530 | ||
531 | if (geteuid()) { | |
532 | err_msg("osnoise needs root permission\n"); | |
533 | exit(EXIT_FAILURE); | |
534 | } | |
535 | ||
536 | return params; | |
537 | } | |
538 | ||
539 | /* | |
540 | * osnoise_top_apply_config - apply the top configs to the initialized tool | |
541 | */ | |
542 | static int | |
543 | osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *params) | |
544 | { | |
545 | int retval; | |
546 | ||
547 | if (!params->sleep_time) | |
548 | params->sleep_time = 1; | |
549 | ||
550 | if (params->cpus) { | |
551 | retval = osnoise_set_cpus(tool->context, params->cpus); | |
552 | if (retval) { | |
553 | err_msg("Failed to apply CPUs config\n"); | |
554 | goto out_err; | |
555 | } | |
556 | } | |
557 | ||
558 | if (params->runtime || params->period) { | |
559 | retval = osnoise_set_runtime_period(tool->context, | |
560 | params->runtime, | |
561 | params->period); | |
562 | if (retval) { | |
563 | err_msg("Failed to set runtime and/or period\n"); | |
564 | goto out_err; | |
565 | } | |
566 | } | |
567 | ||
568 | if (params->stop_us) { | |
569 | retval = osnoise_set_stop_us(tool->context, params->stop_us); | |
570 | if (retval) { | |
571 | err_msg("Failed to set stop us\n"); | |
572 | goto out_err; | |
573 | } | |
574 | } | |
575 | ||
576 | if (params->stop_total_us) { | |
577 | retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); | |
578 | if (retval) { | |
579 | err_msg("Failed to set stop total us\n"); | |
580 | goto out_err; | |
581 | } | |
582 | } | |
583 | ||
d635316a DBO |
584 | if (params->threshold) { |
585 | retval = osnoise_set_tracing_thresh(tool->context, params->threshold); | |
586 | if (retval) { | |
587 | err_msg("Failed to set tracing_thresh\n"); | |
588 | goto out_err; | |
589 | } | |
590 | } | |
591 | ||
1f428356 DBO |
592 | if (params->mode == MODE_HWNOISE) { |
593 | retval = osnoise_set_irq_disable(tool->context, 1); | |
594 | if (retval) { | |
595 | err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n"); | |
596 | goto out_err; | |
597 | } | |
598 | } | |
599 | ||
272ced25 DBO |
600 | if (params->hk_cpus) { |
601 | retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), | |
602 | ¶ms->hk_cpu_set); | |
603 | if (retval == -1) { | |
604 | err_msg("Failed to set rtla to the house keeping CPUs\n"); | |
605 | goto out_err; | |
606 | } | |
c58a3f8c DBO |
607 | } else if (params->cpus) { |
608 | /* | |
609 | * Even if the user do not set a house-keeping CPU, try to | |
610 | * move rtla to a CPU set different to the one where the user | |
611 | * set the workload to run. | |
612 | * | |
613 | * No need to check results as this is an automatic attempt. | |
614 | */ | |
615 | auto_house_keeping(¶ms->monitored_cpus); | |
272ced25 DBO |
616 | } |
617 | ||
1eceb2fc DBO |
618 | return 0; |
619 | ||
620 | out_err: | |
621 | return -1; | |
622 | } | |
623 | ||
624 | /* | |
625 | * osnoise_init_top - initialize a osnoise top tool with parameters | |
626 | */ | |
627 | struct osnoise_tool *osnoise_init_top(struct osnoise_top_params *params) | |
628 | { | |
629 | struct osnoise_tool *tool; | |
630 | int nr_cpus; | |
631 | ||
632 | nr_cpus = sysconf(_SC_NPROCESSORS_CONF); | |
633 | ||
634 | tool = osnoise_init_tool("osnoise_top"); | |
635 | if (!tool) | |
636 | return NULL; | |
637 | ||
638 | tool->data = osnoise_alloc_top(nr_cpus); | |
639 | if (!tool->data) | |
640 | goto out_err; | |
641 | ||
642 | tool->params = params; | |
643 | ||
644 | tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise", | |
645 | osnoise_top_handler, NULL); | |
646 | ||
647 | return tool; | |
648 | ||
649 | out_err: | |
650 | osnoise_free_top(tool->data); | |
651 | osnoise_destroy_tool(tool); | |
652 | return NULL; | |
653 | } | |
654 | ||
655 | static int stop_tracing; | |
656 | static void stop_top(int sig) | |
657 | { | |
658 | stop_tracing = 1; | |
659 | } | |
660 | ||
661 | /* | |
662 | * osnoise_top_set_signals - handles the signal to stop the tool | |
663 | */ | |
664 | static void osnoise_top_set_signals(struct osnoise_top_params *params) | |
665 | { | |
666 | signal(SIGINT, stop_top); | |
667 | if (params->duration) { | |
668 | signal(SIGALRM, stop_top); | |
669 | alarm(params->duration); | |
670 | } | |
671 | } | |
672 | ||
673 | int osnoise_top_main(int argc, char **argv) | |
674 | { | |
675 | struct osnoise_top_params *params; | |
4bbf59a9 DBO |
676 | struct osnoise_tool *record = NULL; |
677 | struct osnoise_tool *tool = NULL; | |
1eceb2fc | 678 | struct trace_instance *trace; |
1eceb2fc DBO |
679 | int return_value = 1; |
680 | int retval; | |
681 | ||
682 | params = osnoise_top_parse_args(argc, argv); | |
683 | if (!params) | |
684 | exit(1); | |
685 | ||
686 | tool = osnoise_init_top(params); | |
687 | if (!tool) { | |
688 | err_msg("Could not init osnoise top\n"); | |
689 | goto out_exit; | |
690 | } | |
691 | ||
692 | retval = osnoise_top_apply_config(tool, params); | |
693 | if (retval) { | |
694 | err_msg("Could not apply config\n"); | |
2a6b52ed | 695 | goto out_free; |
1eceb2fc DBO |
696 | } |
697 | ||
698 | trace = &tool->trace; | |
699 | ||
700 | retval = enable_osnoise(trace); | |
701 | if (retval) { | |
702 | err_msg("Failed to enable osnoise tracer\n"); | |
2a6b52ed | 703 | goto out_free; |
1eceb2fc DBO |
704 | } |
705 | ||
706 | if (params->set_sched) { | |
707 | retval = set_comm_sched_attr("osnoise/", ¶ms->sched_param); | |
708 | if (retval) { | |
709 | err_msg("Failed to set sched parameters\n"); | |
2a6b52ed | 710 | goto out_free; |
1eceb2fc DBO |
711 | } |
712 | } | |
713 | ||
a957cbc0 DBO |
714 | if (params->cgroup) { |
715 | retval = set_comm_cgroup("osnoise/", params->cgroup_name); | |
716 | if (!retval) { | |
717 | err_msg("Failed to move threads to cgroup\n"); | |
718 | goto out_free; | |
719 | } | |
720 | } | |
721 | ||
1eceb2fc DBO |
722 | if (params->trace_output) { |
723 | record = osnoise_init_trace_tool("osnoise"); | |
724 | if (!record) { | |
725 | err_msg("Failed to enable the trace instance\n"); | |
2a6b52ed | 726 | goto out_free; |
1eceb2fc | 727 | } |
51d64c3a DBO |
728 | |
729 | if (params->events) { | |
730 | retval = trace_events_enable(&record->trace, params->events); | |
731 | if (retval) | |
732 | goto out_top; | |
733 | } | |
e9a4062e DBO |
734 | |
735 | if (params->buffer_size > 0) { | |
736 | retval = trace_set_buffer_size(&record->trace, params->buffer_size); | |
737 | if (retval) | |
738 | goto out_top; | |
739 | } | |
57cf76ec | 740 | } |
51d64c3a | 741 | |
57cf76ec DBO |
742 | /* |
743 | * Start the tracer here, after having set all instances. | |
744 | * | |
745 | * Let the trace instance start first for the case of hitting a stop | |
746 | * tracing while enabling other instances. The trace instance is the | |
747 | * one with most valuable information. | |
748 | */ | |
749 | if (params->trace_output) | |
1eceb2fc | 750 | trace_instance_start(&record->trace); |
57cf76ec | 751 | trace_instance_start(trace); |
1eceb2fc | 752 | |
cdbf7196 DBO |
753 | if (params->warmup > 0) { |
754 | debug_msg("Warming up for %d seconds\n", params->warmup); | |
755 | sleep(params->warmup); | |
756 | if (stop_tracing) | |
757 | goto out_top; | |
758 | ||
759 | /* | |
760 | * Clean up the buffer. The osnoise workload do not run | |
761 | * with tracing off to avoid creating a performance penalty | |
762 | * when not needed. | |
763 | */ | |
764 | retval = tracefs_instance_file_write(trace->inst, "trace", ""); | |
765 | if (retval < 0) { | |
766 | debug_msg("Error cleaning up the buffer"); | |
767 | goto out_top; | |
768 | } | |
769 | ||
770 | } | |
771 | ||
1eceb2fc DBO |
772 | tool->start_time = time(NULL); |
773 | osnoise_top_set_signals(params); | |
774 | ||
75016ca3 | 775 | while (!stop_tracing) { |
1eceb2fc DBO |
776 | sleep(params->sleep_time); |
777 | ||
778 | retval = tracefs_iterate_raw_events(trace->tep, | |
779 | trace->inst, | |
780 | NULL, | |
781 | 0, | |
782 | collect_registered_events, | |
783 | trace); | |
784 | if (retval < 0) { | |
785 | err_msg("Error iterating on events\n"); | |
786 | goto out_top; | |
787 | } | |
788 | ||
789 | if (!params->quiet) | |
790 | osnoise_print_stats(params, tool); | |
791 | ||
28d2160c | 792 | if (trace_is_off(&tool->trace, &record->trace)) |
1eceb2fc DBO |
793 | break; |
794 | ||
75016ca3 | 795 | } |
1eceb2fc DBO |
796 | |
797 | osnoise_print_stats(params, tool); | |
798 | ||
799 | return_value = 0; | |
800 | ||
28d2160c | 801 | if (trace_is_off(&tool->trace, &record->trace)) { |
1eceb2fc DBO |
802 | printf("osnoise hit stop tracing\n"); |
803 | if (params->trace_output) { | |
804 | printf(" Saving trace to %s\n", params->trace_output); | |
805 | save_trace_to_file(record->trace.inst, params->trace_output); | |
806 | } | |
807 | } | |
808 | ||
809 | out_top: | |
51d64c3a DBO |
810 | trace_events_destroy(&record->trace, params->events); |
811 | params->events = NULL; | |
2a6b52ed | 812 | out_free: |
1eceb2fc | 813 | osnoise_free_top(tool->data); |
4bbf59a9 | 814 | osnoise_destroy_tool(record); |
1eceb2fc | 815 | osnoise_destroy_tool(tool); |
316f7101 | 816 | free(params); |
1eceb2fc DBO |
817 | out_exit: |
818 | exit(return_value); | |
819 | } |