Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/tracing/rtla/src/osnoise_top.c
29524 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <[email protected]>
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 <stdio.h>
13
#include <time.h>
14
15
#include "osnoise.h"
16
17
struct osnoise_top_cpu {
18
unsigned long long sum_runtime;
19
unsigned long long sum_noise;
20
unsigned long long max_noise;
21
unsigned long long max_sample;
22
23
unsigned long long hw_count;
24
unsigned long long nmi_count;
25
unsigned long long irq_count;
26
unsigned long long softirq_count;
27
unsigned long long thread_count;
28
29
int sum_cycles;
30
};
31
32
struct osnoise_top_data {
33
struct osnoise_top_cpu *cpu_data;
34
int nr_cpus;
35
};
36
37
/*
38
* osnoise_free_top - free runtime data
39
*/
40
static void osnoise_free_top(struct osnoise_top_data *data)
41
{
42
free(data->cpu_data);
43
free(data);
44
}
45
46
static void osnoise_free_top_tool(struct osnoise_tool *tool)
47
{
48
osnoise_free_top(tool->data);
49
}
50
51
/*
52
* osnoise_alloc_histogram - alloc runtime data
53
*/
54
static struct osnoise_top_data *osnoise_alloc_top(int nr_cpus)
55
{
56
struct osnoise_top_data *data;
57
58
data = calloc(1, sizeof(*data));
59
if (!data)
60
return NULL;
61
62
data->nr_cpus = nr_cpus;
63
64
/* one set of histograms per CPU */
65
data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus);
66
if (!data->cpu_data)
67
goto cleanup;
68
69
return data;
70
71
cleanup:
72
osnoise_free_top(data);
73
return NULL;
74
}
75
76
/*
77
* osnoise_top_handler - this is the handler for osnoise tracer events
78
*/
79
static int
80
osnoise_top_handler(struct trace_seq *s, struct tep_record *record,
81
struct tep_event *event, void *context)
82
{
83
struct trace_instance *trace = context;
84
struct osnoise_tool *tool;
85
unsigned long long val;
86
struct osnoise_top_cpu *cpu_data;
87
struct osnoise_top_data *data;
88
int cpu = record->cpu;
89
90
tool = container_of(trace, struct osnoise_tool, trace);
91
92
data = tool->data;
93
cpu_data = &data->cpu_data[cpu];
94
95
cpu_data->sum_cycles++;
96
97
tep_get_field_val(s, event, "runtime", record, &val, 1);
98
update_sum(&cpu_data->sum_runtime, &val);
99
100
tep_get_field_val(s, event, "noise", record, &val, 1);
101
update_max(&cpu_data->max_noise, &val);
102
update_sum(&cpu_data->sum_noise, &val);
103
104
tep_get_field_val(s, event, "max_sample", record, &val, 1);
105
update_max(&cpu_data->max_sample, &val);
106
107
tep_get_field_val(s, event, "hw_count", record, &val, 1);
108
update_sum(&cpu_data->hw_count, &val);
109
110
tep_get_field_val(s, event, "nmi_count", record, &val, 1);
111
update_sum(&cpu_data->nmi_count, &val);
112
113
tep_get_field_val(s, event, "irq_count", record, &val, 1);
114
update_sum(&cpu_data->irq_count, &val);
115
116
tep_get_field_val(s, event, "softirq_count", record, &val, 1);
117
update_sum(&cpu_data->softirq_count, &val);
118
119
tep_get_field_val(s, event, "thread_count", record, &val, 1);
120
update_sum(&cpu_data->thread_count, &val);
121
122
return 0;
123
}
124
125
/*
126
* osnoise_top_header - print the header of the tool output
127
*/
128
static void osnoise_top_header(struct osnoise_tool *top)
129
{
130
struct osnoise_params *params = to_osnoise_params(top->params);
131
struct trace_seq *s = top->trace.seq;
132
bool pretty = params->common.pretty_output;
133
char duration[26];
134
135
get_duration(top->start_time, duration, sizeof(duration));
136
137
if (pretty)
138
trace_seq_printf(s, "\033[2;37;40m");
139
140
trace_seq_printf(s, " ");
141
142
if (params->mode == MODE_OSNOISE) {
143
trace_seq_printf(s, "Operating System Noise");
144
trace_seq_printf(s, " ");
145
} else if (params->mode == MODE_HWNOISE) {
146
trace_seq_printf(s, "Hardware-related Noise");
147
}
148
149
trace_seq_printf(s, " ");
150
151
if (pretty)
152
trace_seq_printf(s, "\033[0;0;0m");
153
trace_seq_printf(s, "\n");
154
155
trace_seq_printf(s, "duration: %9s | time is in us\n", duration);
156
157
if (pretty)
158
trace_seq_printf(s, "\033[2;30;47m");
159
160
trace_seq_printf(s, "CPU Period Runtime ");
161
trace_seq_printf(s, " Noise ");
162
trace_seq_printf(s, " %% CPU Aval ");
163
trace_seq_printf(s, " Max Noise Max Single ");
164
trace_seq_printf(s, " HW NMI");
165
166
if (params->mode == MODE_HWNOISE)
167
goto eol;
168
169
trace_seq_printf(s, " IRQ Softirq Thread");
170
171
eol:
172
if (pretty)
173
trace_seq_printf(s, "\033[0;0;0m");
174
trace_seq_printf(s, "\n");
175
}
176
177
/*
178
* clear_terminal - clears the output terminal
179
*/
180
static void clear_terminal(struct trace_seq *seq)
181
{
182
if (!config_debug)
183
trace_seq_printf(seq, "\033c");
184
}
185
186
/*
187
* osnoise_top_print - prints the output of a given CPU
188
*/
189
static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
190
{
191
struct osnoise_params *params = to_osnoise_params(tool->params);
192
struct trace_seq *s = tool->trace.seq;
193
struct osnoise_top_cpu *cpu_data;
194
struct osnoise_top_data *data;
195
int percentage;
196
int decimal;
197
198
data = tool->data;
199
cpu_data = &data->cpu_data[cpu];
200
201
if (!cpu_data->sum_runtime)
202
return;
203
204
percentage = ((cpu_data->sum_runtime - cpu_data->sum_noise) * 10000000)
205
/ cpu_data->sum_runtime;
206
decimal = percentage % 100000;
207
percentage = percentage / 100000;
208
209
trace_seq_printf(s, "%3d #%-6d %12llu ", cpu, cpu_data->sum_cycles, cpu_data->sum_runtime);
210
trace_seq_printf(s, "%12llu ", cpu_data->sum_noise);
211
trace_seq_printf(s, " %3d.%05d", percentage, decimal);
212
trace_seq_printf(s, "%12llu %12llu", cpu_data->max_noise, cpu_data->max_sample);
213
214
trace_seq_printf(s, "%12llu ", cpu_data->hw_count);
215
trace_seq_printf(s, "%12llu ", cpu_data->nmi_count);
216
217
if (params->mode == MODE_HWNOISE) {
218
trace_seq_printf(s, "\n");
219
return;
220
}
221
222
trace_seq_printf(s, "%12llu ", cpu_data->irq_count);
223
trace_seq_printf(s, "%12llu ", cpu_data->softirq_count);
224
trace_seq_printf(s, "%12llu\n", cpu_data->thread_count);
225
}
226
227
/*
228
* osnoise_print_stats - print data for all cpus
229
*/
230
static void
231
osnoise_print_stats(struct osnoise_tool *top)
232
{
233
struct osnoise_params *params = to_osnoise_params(top->params);
234
struct trace_instance *trace = &top->trace;
235
static int nr_cpus = -1;
236
int i;
237
238
if (nr_cpus == -1)
239
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
240
241
if (!params->common.quiet)
242
clear_terminal(trace->seq);
243
244
osnoise_top_header(top);
245
246
for (i = 0; i < nr_cpus; i++) {
247
if (params->common.cpus && !CPU_ISSET(i, &params->common.monitored_cpus))
248
continue;
249
osnoise_top_print(top, i);
250
}
251
252
trace_seq_do_printf(trace->seq);
253
trace_seq_reset(trace->seq);
254
osnoise_report_missed_events(top);
255
}
256
257
/*
258
* osnoise_top_usage - prints osnoise top usage message
259
*/
260
static void osnoise_top_usage(struct osnoise_params *params, char *usage)
261
{
262
int i;
263
264
static const char * const msg[] = {
265
" [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
266
" [-T us] [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
267
" [-c cpu-list] [-H cpu-list] [-P priority] [-C[=cgroup_name]] [--warm-up s]",
268
"",
269
" -h/--help: print this menu",
270
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
271
" -p/--period us: osnoise period in us",
272
" -r/--runtime us: osnoise runtime in us",
273
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
274
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
275
" -T/--threshold us: the minimum delta to be considered a noise",
276
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
277
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
278
" -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
279
" -d/--duration time[s|m|h|d]: duration of the session",
280
" -D/--debug: print debug info",
281
" -t/--trace[file]: save the stopped trace to [file|osnoise_trace.txt]",
282
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
283
" --filter <filter>: enable a trace event filter to the previous -e event",
284
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
285
" -q/--quiet print only a summary at the end",
286
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
287
" o:prio - use SCHED_OTHER with prio",
288
" r:prio - use SCHED_RR with prio",
289
" f:prio - use SCHED_FIFO with prio",
290
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
291
" in nanoseconds",
292
" --warm-up s: let the workload run for s seconds before collecting data",
293
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
294
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
295
" --on-end: define action to be executed at measurement end, multiple are allowed",
296
NULL,
297
};
298
299
if (usage)
300
fprintf(stderr, "%s\n", usage);
301
302
if (params->mode == MODE_OSNOISE) {
303
fprintf(stderr,
304
"rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n",
305
VERSION);
306
307
fprintf(stderr, " usage: rtla osnoise [top]");
308
}
309
310
if (params->mode == MODE_HWNOISE) {
311
fprintf(stderr,
312
"rtla hwnoise: a summary of hardware-related noise (version %s)\n",
313
VERSION);
314
315
fprintf(stderr, " usage: rtla hwnoise");
316
}
317
318
for (i = 0; msg[i]; i++)
319
fprintf(stderr, "%s\n", msg[i]);
320
321
if (usage)
322
exit(EXIT_FAILURE);
323
324
exit(EXIT_SUCCESS);
325
}
326
327
/*
328
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
329
*/
330
struct common_params *osnoise_top_parse_args(int argc, char **argv)
331
{
332
struct osnoise_params *params;
333
struct trace_events *tevent;
334
int retval;
335
int c;
336
char *trace_output = NULL;
337
338
params = calloc(1, sizeof(*params));
339
if (!params)
340
exit(1);
341
342
actions_init(&params->common.threshold_actions);
343
actions_init(&params->common.end_actions);
344
345
if (strcmp(argv[0], "hwnoise") == 0) {
346
params->mode = MODE_HWNOISE;
347
/*
348
* Reduce CPU usage for 75% to avoid killing the system.
349
*/
350
params->runtime = 750000;
351
params->period = 1000000;
352
}
353
354
while (1) {
355
static struct option long_options[] = {
356
{"auto", required_argument, 0, 'a'},
357
{"cpus", required_argument, 0, 'c'},
358
{"cgroup", optional_argument, 0, 'C'},
359
{"debug", no_argument, 0, 'D'},
360
{"duration", required_argument, 0, 'd'},
361
{"event", required_argument, 0, 'e'},
362
{"house-keeping", required_argument, 0, 'H'},
363
{"help", no_argument, 0, 'h'},
364
{"period", required_argument, 0, 'p'},
365
{"priority", required_argument, 0, 'P'},
366
{"quiet", no_argument, 0, 'q'},
367
{"runtime", required_argument, 0, 'r'},
368
{"stop", required_argument, 0, 's'},
369
{"stop-total", required_argument, 0, 'S'},
370
{"threshold", required_argument, 0, 'T'},
371
{"trace", optional_argument, 0, 't'},
372
{"trigger", required_argument, 0, '0'},
373
{"filter", required_argument, 0, '1'},
374
{"warm-up", required_argument, 0, '2'},
375
{"trace-buffer-size", required_argument, 0, '3'},
376
{"on-threshold", required_argument, 0, '4'},
377
{"on-end", required_argument, 0, '5'},
378
{0, 0, 0, 0}
379
};
380
381
/* getopt_long stores the option index here. */
382
int option_index = 0;
383
384
c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:",
385
long_options, &option_index);
386
387
/* Detect the end of the options. */
388
if (c == -1)
389
break;
390
391
switch (c) {
392
case 'a':
393
/* set sample stop to auto_thresh */
394
params->common.stop_us = get_llong_from_str(optarg);
395
396
/* set sample threshold to 1 */
397
params->threshold = 1;
398
399
/* set trace */
400
trace_output = "osnoise_trace.txt";
401
402
break;
403
case 'c':
404
retval = parse_cpu_set(optarg, &params->common.monitored_cpus);
405
if (retval)
406
osnoise_top_usage(params, "\nInvalid -c cpu list\n");
407
params->common.cpus = optarg;
408
break;
409
case 'C':
410
params->common.cgroup = 1;
411
if (!optarg) {
412
/* will inherit this cgroup */
413
params->common.cgroup_name = NULL;
414
} else if (*optarg == '=') {
415
/* skip the = */
416
params->common.cgroup_name = ++optarg;
417
}
418
break;
419
case 'D':
420
config_debug = 1;
421
break;
422
case 'd':
423
params->common.duration = parse_seconds_duration(optarg);
424
if (!params->common.duration)
425
osnoise_top_usage(params, "Invalid -d duration\n");
426
break;
427
case 'e':
428
tevent = trace_event_alloc(optarg);
429
if (!tevent) {
430
err_msg("Error alloc trace event");
431
exit(EXIT_FAILURE);
432
}
433
434
if (params->common.events)
435
tevent->next = params->common.events;
436
params->common.events = tevent;
437
438
break;
439
case 'h':
440
case '?':
441
osnoise_top_usage(params, NULL);
442
break;
443
case 'H':
444
params->common.hk_cpus = 1;
445
retval = parse_cpu_set(optarg, &params->common.hk_cpu_set);
446
if (retval) {
447
err_msg("Error parsing house keeping CPUs\n");
448
exit(EXIT_FAILURE);
449
}
450
break;
451
case 'p':
452
params->period = get_llong_from_str(optarg);
453
if (params->period > 10000000)
454
osnoise_top_usage(params, "Period longer than 10 s\n");
455
break;
456
case 'P':
457
retval = parse_prio(optarg, &params->common.sched_param);
458
if (retval == -1)
459
osnoise_top_usage(params, "Invalid -P priority");
460
params->common.set_sched = 1;
461
break;
462
case 'q':
463
params->common.quiet = 1;
464
break;
465
case 'r':
466
params->runtime = get_llong_from_str(optarg);
467
if (params->runtime < 100)
468
osnoise_top_usage(params, "Runtime shorter than 100 us\n");
469
break;
470
case 's':
471
params->common.stop_us = get_llong_from_str(optarg);
472
break;
473
case 'S':
474
params->common.stop_total_us = get_llong_from_str(optarg);
475
break;
476
case 't':
477
if (optarg) {
478
if (optarg[0] == '=')
479
trace_output = &optarg[1];
480
else
481
trace_output = &optarg[0];
482
} else if (optind < argc && argv[optind][0] != '-')
483
trace_output = argv[optind];
484
else
485
trace_output = "osnoise_trace.txt";
486
break;
487
case 'T':
488
params->threshold = get_llong_from_str(optarg);
489
break;
490
case '0': /* trigger */
491
if (params->common.events) {
492
retval = trace_event_add_trigger(params->common.events, optarg);
493
if (retval) {
494
err_msg("Error adding trigger %s\n", optarg);
495
exit(EXIT_FAILURE);
496
}
497
} else {
498
osnoise_top_usage(params, "--trigger requires a previous -e\n");
499
}
500
break;
501
case '1': /* filter */
502
if (params->common.events) {
503
retval = trace_event_add_filter(params->common.events, optarg);
504
if (retval) {
505
err_msg("Error adding filter %s\n", optarg);
506
exit(EXIT_FAILURE);
507
}
508
} else {
509
osnoise_top_usage(params, "--filter requires a previous -e\n");
510
}
511
break;
512
case '2':
513
params->common.warmup = get_llong_from_str(optarg);
514
break;
515
case '3':
516
params->common.buffer_size = get_llong_from_str(optarg);
517
break;
518
case '4':
519
retval = actions_parse(&params->common.threshold_actions, optarg,
520
"osnoise_trace.txt");
521
if (retval) {
522
err_msg("Invalid action %s\n", optarg);
523
exit(EXIT_FAILURE);
524
}
525
break;
526
case '5':
527
retval = actions_parse(&params->common.end_actions, optarg,
528
"osnoise_trace.txt");
529
if (retval) {
530
err_msg("Invalid action %s\n", optarg);
531
exit(EXIT_FAILURE);
532
}
533
break;
534
default:
535
osnoise_top_usage(params, "Invalid option");
536
}
537
}
538
539
if (trace_output)
540
actions_add_trace_output(&params->common.threshold_actions, trace_output);
541
542
if (geteuid()) {
543
err_msg("osnoise needs root permission\n");
544
exit(EXIT_FAILURE);
545
}
546
547
return &params->common;
548
}
549
550
/*
551
* osnoise_top_apply_config - apply the top configs to the initialized tool
552
*/
553
static int
554
osnoise_top_apply_config(struct osnoise_tool *tool)
555
{
556
struct osnoise_params *params = to_osnoise_params(tool->params);
557
int retval;
558
559
retval = osnoise_apply_config(tool, params);
560
if (retval)
561
goto out_err;
562
563
if (params->mode == MODE_HWNOISE) {
564
retval = osnoise_set_irq_disable(tool->context, 1);
565
if (retval) {
566
err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n");
567
goto out_err;
568
}
569
}
570
571
if (isatty(STDOUT_FILENO) && !params->common.quiet)
572
params->common.pretty_output = 1;
573
574
return 0;
575
576
out_err:
577
return -1;
578
}
579
580
/*
581
* osnoise_init_top - initialize a osnoise top tool with parameters
582
*/
583
struct osnoise_tool *osnoise_init_top(struct common_params *params)
584
{
585
struct osnoise_tool *tool;
586
int nr_cpus;
587
588
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
589
590
tool = osnoise_init_tool("osnoise_top");
591
if (!tool)
592
return NULL;
593
594
tool->data = osnoise_alloc_top(nr_cpus);
595
if (!tool->data) {
596
osnoise_destroy_tool(tool);
597
return NULL;
598
}
599
600
tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise",
601
osnoise_top_handler, NULL);
602
603
return tool;
604
}
605
606
struct tool_ops osnoise_top_ops = {
607
.tracer = "osnoise",
608
.comm_prefix = "osnoise/",
609
.parse_args = osnoise_top_parse_args,
610
.init_tool = osnoise_init_top,
611
.apply_config = osnoise_top_apply_config,
612
.enable = osnoise_enable,
613
.main = top_main_loop,
614
.print_stats = osnoise_print_stats,
615
.free = osnoise_free_top_tool,
616
};
617
618