Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/tracing/rtla/src/timerlat.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
#define _GNU_SOURCE
6
#include <sys/types.h>
7
#include <sys/stat.h>
8
#include <pthread.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <unistd.h>
12
#include <errno.h>
13
#include <fcntl.h>
14
#include <stdio.h>
15
#include <sched.h>
16
17
#include "timerlat.h"
18
#include "timerlat_aa.h"
19
#include "timerlat_bpf.h"
20
21
#define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */
22
23
static int dma_latency_fd = -1;
24
25
/*
26
* timerlat_apply_config - apply common configs to the initialized tool
27
*/
28
int
29
timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
30
{
31
int retval;
32
33
/*
34
* Try to enable BPF, unless disabled explicitly.
35
* If BPF enablement fails, fall back to tracefs mode.
36
*/
37
if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
38
debug_msg("RTLA_NO_BPF set, disabling BPF\n");
39
params->mode = TRACING_MODE_TRACEFS;
40
} else if (!tep_find_event_by_name(tool->trace.tep, "osnoise", "timerlat_sample")) {
41
debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
42
params->mode = TRACING_MODE_TRACEFS;
43
} else {
44
retval = timerlat_bpf_init(params);
45
if (retval) {
46
debug_msg("Could not enable BPF\n");
47
params->mode = TRACING_MODE_TRACEFS;
48
}
49
}
50
51
if (params->mode != TRACING_MODE_BPF) {
52
/*
53
* In tracefs and mixed mode, timerlat tracer handles stopping
54
* on threshold
55
*/
56
retval = osnoise_set_stop_us(tool->context, params->common.stop_us);
57
if (retval) {
58
err_msg("Failed to set stop us\n");
59
goto out_err;
60
}
61
62
retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us);
63
if (retval) {
64
err_msg("Failed to set stop total us\n");
65
goto out_err;
66
}
67
}
68
69
70
retval = osnoise_set_timerlat_period_us(tool->context,
71
params->timerlat_period_us ?
72
params->timerlat_period_us :
73
DEFAULT_TIMERLAT_PERIOD);
74
if (retval) {
75
err_msg("Failed to set timerlat period\n");
76
goto out_err;
77
}
78
79
80
retval = osnoise_set_print_stack(tool->context, params->print_stack);
81
if (retval) {
82
err_msg("Failed to set print stack\n");
83
goto out_err;
84
}
85
86
/*
87
* If the user did not specify a type of thread, try user-threads first.
88
* Fall back to kernel threads otherwise.
89
*/
90
if (!params->common.kernel_workload && !params->common.user_data) {
91
retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");
92
if (retval) {
93
debug_msg("User-space interface detected, setting user-threads\n");
94
params->common.user_workload = 1;
95
params->common.user_data = 1;
96
} else {
97
debug_msg("User-space interface not detected, setting kernel-threads\n");
98
params->common.kernel_workload = 1;
99
}
100
}
101
102
return common_apply_config(tool, &params->common);
103
104
out_err:
105
return -1;
106
}
107
108
int timerlat_enable(struct osnoise_tool *tool)
109
{
110
struct timerlat_params *params = to_timerlat_params(tool->params);
111
int retval, nr_cpus, i;
112
113
if (params->dma_latency >= 0) {
114
dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
115
if (dma_latency_fd < 0) {
116
err_msg("Could not set /dev/cpu_dma_latency.\n");
117
return -1;
118
}
119
}
120
121
if (params->deepest_idle_state >= -1) {
122
if (!have_libcpupower_support()) {
123
err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n");
124
return -1;
125
}
126
127
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
128
129
for (i = 0; i < nr_cpus; i++) {
130
if (params->common.cpus && !CPU_ISSET(i, &params->common.monitored_cpus))
131
continue;
132
if (save_cpu_idle_disable_state(i) < 0) {
133
err_msg("Could not save cpu idle state.\n");
134
return -1;
135
}
136
if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
137
err_msg("Could not set deepest cpu idle state.\n");
138
return -1;
139
}
140
}
141
}
142
143
if (!params->no_aa) {
144
tool->aa = osnoise_init_tool("timerlat_aa");
145
if (!tool->aa)
146
return -1;
147
148
retval = timerlat_aa_init(tool->aa, params->dump_tasks);
149
if (retval) {
150
err_msg("Failed to enable the auto analysis instance\n");
151
return retval;
152
}
153
154
retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat");
155
if (retval) {
156
err_msg("Failed to enable aa tracer\n");
157
return retval;
158
}
159
}
160
161
if (params->common.warmup > 0) {
162
debug_msg("Warming up for %d seconds\n", params->common.warmup);
163
sleep(params->common.warmup);
164
if (stop_tracing)
165
return -1;
166
}
167
168
/*
169
* Start the tracers here, after having set all instances.
170
*
171
* Let the trace instance start first for the case of hitting a stop
172
* tracing while enabling other instances. The trace instance is the
173
* one with most valuable information.
174
*/
175
if (tool->record)
176
trace_instance_start(&tool->record->trace);
177
if (!params->no_aa)
178
trace_instance_start(&tool->aa->trace);
179
if (params->mode == TRACING_MODE_TRACEFS) {
180
trace_instance_start(&tool->trace);
181
} else {
182
retval = timerlat_bpf_attach();
183
if (retval) {
184
err_msg("Error attaching BPF program\n");
185
return retval;
186
}
187
}
188
189
return 0;
190
}
191
192
void timerlat_analyze(struct osnoise_tool *tool, bool stopped)
193
{
194
struct timerlat_params *params = to_timerlat_params(tool->params);
195
196
if (stopped) {
197
if (!params->no_aa)
198
timerlat_auto_analysis(params->common.stop_us,
199
params->common.stop_total_us);
200
} else if (params->common.aa_only) {
201
char *max_lat;
202
203
/*
204
* If the trace did not stop with --aa-only, at least print
205
* the max known latency.
206
*/
207
max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL);
208
if (max_lat) {
209
printf(" Max latency was %s\n", max_lat);
210
free(max_lat);
211
}
212
}
213
}
214
215
void timerlat_free(struct osnoise_tool *tool)
216
{
217
struct timerlat_params *params = to_timerlat_params(tool->params);
218
int nr_cpus, i;
219
220
timerlat_aa_destroy();
221
if (dma_latency_fd >= 0)
222
close(dma_latency_fd);
223
if (params->deepest_idle_state >= -1) {
224
for (i = 0; i < nr_cpus; i++) {
225
if (params->common.cpus &&
226
!CPU_ISSET(i, &params->common.monitored_cpus))
227
continue;
228
restore_cpu_idle_disable_state(i);
229
}
230
}
231
232
osnoise_destroy_tool(tool->aa);
233
234
if (params->mode != TRACING_MODE_TRACEFS)
235
timerlat_bpf_destroy();
236
free_cpu_idle_disable_states();
237
}
238
239
static void timerlat_usage(int err)
240
{
241
int i;
242
243
static const char * const msg[] = {
244
"",
245
"timerlat version " VERSION,
246
"",
247
" usage: [rtla] timerlat [MODE] ...",
248
"",
249
" modes:",
250
" top - prints the summary from timerlat tracer",
251
" hist - prints a histogram of timer latencies",
252
"",
253
"if no MODE is given, the top mode is called, passing the arguments",
254
NULL,
255
};
256
257
for (i = 0; msg[i]; i++)
258
fprintf(stderr, "%s\n", msg[i]);
259
exit(err);
260
}
261
262
int timerlat_main(int argc, char *argv[])
263
{
264
if (argc == 0)
265
goto usage;
266
267
/*
268
* if timerlat was called without any argument, run the
269
* default cmdline.
270
*/
271
if (argc == 1) {
272
run_tool(&timerlat_top_ops, argc, argv);
273
exit(0);
274
}
275
276
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
277
timerlat_usage(0);
278
} else if (strncmp(argv[1], "-", 1) == 0) {
279
/* the user skipped the tool, call the default one */
280
run_tool(&timerlat_top_ops, argc, argv);
281
exit(0);
282
} else if (strcmp(argv[1], "top") == 0) {
283
run_tool(&timerlat_top_ops, argc-1, &argv[1]);
284
exit(0);
285
} else if (strcmp(argv[1], "hist") == 0) {
286
run_tool(&timerlat_hist_ops, argc-1, &argv[1]);
287
exit(0);
288
}
289
290
usage:
291
timerlat_usage(1);
292
exit(1);
293
}
294
295