Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/tracing/rtla/src/osnoise_top.c
50902 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_each_monitored_cpu(i, nr_cpus, &params->common) {
247
osnoise_top_print(top, i);
248
}
249
250
trace_seq_do_printf(trace->seq);
251
trace_seq_reset(trace->seq);
252
osnoise_report_missed_events(top);
253
}
254
255
/*
256
* osnoise_top_usage - prints osnoise top usage message
257
*/
258
static void osnoise_top_usage(struct osnoise_params *params)
259
{
260
int i;
261
262
static const char * const msg[] = {
263
" [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
264
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
265
" [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]",
266
"",
267
" -h/--help: print this menu",
268
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
269
" -p/--period us: osnoise period in us",
270
" -r/--runtime us: osnoise runtime in us",
271
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
272
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
273
" -T/--threshold us: the minimum delta to be considered a noise",
274
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
275
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
276
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
277
" -d/--duration time[s|m|h|d]: duration of the session",
278
" -D/--debug: print debug info",
279
" -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]",
280
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
281
" --filter <filter>: enable a trace event filter to the previous -e event",
282
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
283
" -q/--quiet print only a summary at the end",
284
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
285
" o:prio - use SCHED_OTHER with prio",
286
" r:prio - use SCHED_RR with prio",
287
" f:prio - use SCHED_FIFO with prio",
288
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
289
" in nanoseconds",
290
" --warm-up s: let the workload run for s seconds before collecting data",
291
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
292
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
293
" --on-end: define action to be executed at measurement end, multiple are allowed",
294
NULL,
295
};
296
297
if (params->mode == MODE_OSNOISE) {
298
fprintf(stderr,
299
"rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n",
300
VERSION);
301
302
fprintf(stderr, " usage: rtla osnoise [top]");
303
}
304
305
if (params->mode == MODE_HWNOISE) {
306
fprintf(stderr,
307
"rtla hwnoise: a summary of hardware-related noise (version %s)\n",
308
VERSION);
309
310
fprintf(stderr, " usage: rtla hwnoise");
311
}
312
313
for (i = 0; msg[i]; i++)
314
fprintf(stderr, "%s\n", msg[i]);
315
316
exit(EXIT_SUCCESS);
317
}
318
319
/*
320
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
321
*/
322
struct common_params *osnoise_top_parse_args(int argc, char **argv)
323
{
324
struct osnoise_params *params;
325
struct trace_events *tevent;
326
int retval;
327
int c;
328
char *trace_output = NULL;
329
330
params = calloc(1, sizeof(*params));
331
if (!params)
332
exit(1);
333
334
actions_init(&params->common.threshold_actions);
335
actions_init(&params->common.end_actions);
336
337
if (strcmp(argv[0], "hwnoise") == 0) {
338
params->mode = MODE_HWNOISE;
339
/*
340
* Reduce CPU usage for 75% to avoid killing the system.
341
*/
342
params->runtime = 750000;
343
params->period = 1000000;
344
}
345
346
while (1) {
347
static struct option long_options[] = {
348
{"auto", required_argument, 0, 'a'},
349
{"cpus", required_argument, 0, 'c'},
350
{"cgroup", optional_argument, 0, 'C'},
351
{"debug", no_argument, 0, 'D'},
352
{"duration", required_argument, 0, 'd'},
353
{"event", required_argument, 0, 'e'},
354
{"house-keeping", required_argument, 0, 'H'},
355
{"help", no_argument, 0, 'h'},
356
{"period", required_argument, 0, 'p'},
357
{"priority", required_argument, 0, 'P'},
358
{"quiet", no_argument, 0, 'q'},
359
{"runtime", required_argument, 0, 'r'},
360
{"stop", required_argument, 0, 's'},
361
{"stop-total", required_argument, 0, 'S'},
362
{"threshold", required_argument, 0, 'T'},
363
{"trace", optional_argument, 0, 't'},
364
{"trigger", required_argument, 0, '0'},
365
{"filter", required_argument, 0, '1'},
366
{"warm-up", required_argument, 0, '2'},
367
{"trace-buffer-size", required_argument, 0, '3'},
368
{"on-threshold", required_argument, 0, '4'},
369
{"on-end", required_argument, 0, '5'},
370
{0, 0, 0, 0}
371
};
372
373
c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:",
374
long_options, NULL);
375
376
/* Detect the end of the options. */
377
if (c == -1)
378
break;
379
380
switch (c) {
381
case 'a':
382
/* set sample stop to auto_thresh */
383
params->common.stop_us = get_llong_from_str(optarg);
384
385
/* set sample threshold to 1 */
386
params->threshold = 1;
387
388
/* set trace */
389
if (!trace_output)
390
trace_output = "osnoise_trace.txt";
391
392
break;
393
case 'c':
394
retval = parse_cpu_set(optarg, &params->common.monitored_cpus);
395
if (retval)
396
fatal("Invalid -c cpu list");
397
params->common.cpus = optarg;
398
break;
399
case 'C':
400
params->common.cgroup = 1;
401
params->common.cgroup_name = parse_optional_arg(argc, argv);
402
break;
403
case 'D':
404
config_debug = 1;
405
break;
406
case 'd':
407
params->common.duration = parse_seconds_duration(optarg);
408
if (!params->common.duration)
409
fatal("Invalid -d duration");
410
break;
411
case 'e':
412
tevent = trace_event_alloc(optarg);
413
if (!tevent)
414
fatal("Error alloc trace event");
415
416
if (params->common.events)
417
tevent->next = params->common.events;
418
params->common.events = tevent;
419
420
break;
421
case 'h':
422
case '?':
423
osnoise_top_usage(params);
424
break;
425
case 'H':
426
params->common.hk_cpus = 1;
427
retval = parse_cpu_set(optarg, &params->common.hk_cpu_set);
428
if (retval)
429
fatal("Error parsing house keeping CPUs");
430
break;
431
case 'p':
432
params->period = get_llong_from_str(optarg);
433
if (params->period > 10000000)
434
fatal("Period longer than 10 s");
435
break;
436
case 'P':
437
retval = parse_prio(optarg, &params->common.sched_param);
438
if (retval == -1)
439
fatal("Invalid -P priority");
440
params->common.set_sched = 1;
441
break;
442
case 'q':
443
params->common.quiet = 1;
444
break;
445
case 'r':
446
params->runtime = get_llong_from_str(optarg);
447
if (params->runtime < 100)
448
fatal("Runtime shorter than 100 us");
449
break;
450
case 's':
451
params->common.stop_us = get_llong_from_str(optarg);
452
break;
453
case 'S':
454
params->common.stop_total_us = get_llong_from_str(optarg);
455
break;
456
case 't':
457
trace_output = parse_optional_arg(argc, argv);
458
if (!trace_output)
459
trace_output = "osnoise_trace.txt";
460
break;
461
case 'T':
462
params->threshold = get_llong_from_str(optarg);
463
break;
464
case '0': /* trigger */
465
if (params->common.events) {
466
retval = trace_event_add_trigger(params->common.events, optarg);
467
if (retval)
468
fatal("Error adding trigger %s", optarg);
469
} else {
470
fatal("--trigger requires a previous -e");
471
}
472
break;
473
case '1': /* filter */
474
if (params->common.events) {
475
retval = trace_event_add_filter(params->common.events, optarg);
476
if (retval)
477
fatal("Error adding filter %s", optarg);
478
} else {
479
fatal("--filter requires a previous -e");
480
}
481
break;
482
case '2':
483
params->common.warmup = get_llong_from_str(optarg);
484
break;
485
case '3':
486
params->common.buffer_size = get_llong_from_str(optarg);
487
break;
488
case '4':
489
retval = actions_parse(&params->common.threshold_actions, optarg,
490
"osnoise_trace.txt");
491
if (retval)
492
fatal("Invalid action %s", optarg);
493
break;
494
case '5':
495
retval = actions_parse(&params->common.end_actions, optarg,
496
"osnoise_trace.txt");
497
if (retval)
498
fatal("Invalid action %s", optarg);
499
break;
500
default:
501
fatal("Invalid option");
502
}
503
}
504
505
if (trace_output)
506
actions_add_trace_output(&params->common.threshold_actions, trace_output);
507
508
if (geteuid())
509
fatal("osnoise needs root permission");
510
511
return &params->common;
512
}
513
514
/*
515
* osnoise_top_apply_config - apply the top configs to the initialized tool
516
*/
517
static int
518
osnoise_top_apply_config(struct osnoise_tool *tool)
519
{
520
struct osnoise_params *params = to_osnoise_params(tool->params);
521
int retval;
522
523
retval = osnoise_apply_config(tool, params);
524
if (retval)
525
goto out_err;
526
527
if (params->mode == MODE_HWNOISE) {
528
retval = osnoise_set_irq_disable(tool->context, 1);
529
if (retval) {
530
err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n");
531
goto out_err;
532
}
533
}
534
535
if (isatty(STDOUT_FILENO) && !params->common.quiet)
536
params->common.pretty_output = 1;
537
538
return 0;
539
540
out_err:
541
return -1;
542
}
543
544
/*
545
* osnoise_init_top - initialize a osnoise top tool with parameters
546
*/
547
struct osnoise_tool *osnoise_init_top(struct common_params *params)
548
{
549
struct osnoise_tool *tool;
550
int nr_cpus;
551
552
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
553
554
tool = osnoise_init_tool("osnoise_top");
555
if (!tool)
556
return NULL;
557
558
tool->data = osnoise_alloc_top(nr_cpus);
559
if (!tool->data) {
560
osnoise_destroy_tool(tool);
561
return NULL;
562
}
563
564
tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise",
565
osnoise_top_handler, NULL);
566
567
return tool;
568
}
569
570
struct tool_ops osnoise_top_ops = {
571
.tracer = "osnoise",
572
.comm_prefix = "osnoise/",
573
.parse_args = osnoise_top_parse_args,
574
.init_tool = osnoise_init_top,
575
.apply_config = osnoise_top_apply_config,
576
.enable = osnoise_enable,
577
.main = top_main_loop,
578
.print_stats = osnoise_print_stats,
579
.free = osnoise_free_top_tool,
580
};
581
582