Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/tracing/rtla/src/timerlat.c
49452 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_each_monitored_cpu(i, nr_cpus, &params->common) {
130
if (save_cpu_idle_disable_state(i) < 0) {
131
err_msg("Could not save cpu idle state.\n");
132
return -1;
133
}
134
if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
135
err_msg("Could not set deepest cpu idle state.\n");
136
return -1;
137
}
138
}
139
}
140
141
if (!params->no_aa) {
142
tool->aa = osnoise_init_tool("timerlat_aa");
143
if (!tool->aa)
144
return -1;
145
146
retval = timerlat_aa_init(tool->aa, params->dump_tasks);
147
if (retval) {
148
err_msg("Failed to enable the auto analysis instance\n");
149
return retval;
150
}
151
152
retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat");
153
if (retval) {
154
err_msg("Failed to enable aa tracer\n");
155
return retval;
156
}
157
}
158
159
if (params->common.warmup > 0) {
160
debug_msg("Warming up for %d seconds\n", params->common.warmup);
161
sleep(params->common.warmup);
162
if (stop_tracing)
163
return -1;
164
}
165
166
/*
167
* Start the tracers here, after having set all instances.
168
*
169
* Let the trace instance start first for the case of hitting a stop
170
* tracing while enabling other instances. The trace instance is the
171
* one with most valuable information.
172
*/
173
if (tool->record)
174
trace_instance_start(&tool->record->trace);
175
if (!params->no_aa)
176
trace_instance_start(&tool->aa->trace);
177
if (params->mode == TRACING_MODE_TRACEFS) {
178
trace_instance_start(&tool->trace);
179
} else {
180
retval = timerlat_bpf_attach();
181
if (retval) {
182
err_msg("Error attaching BPF program\n");
183
return retval;
184
}
185
}
186
187
return 0;
188
}
189
190
void timerlat_analyze(struct osnoise_tool *tool, bool stopped)
191
{
192
struct timerlat_params *params = to_timerlat_params(tool->params);
193
194
if (stopped) {
195
if (!params->no_aa)
196
timerlat_auto_analysis(params->common.stop_us,
197
params->common.stop_total_us);
198
} else if (params->common.aa_only) {
199
char *max_lat;
200
201
/*
202
* If the trace did not stop with --aa-only, at least print
203
* the max known latency.
204
*/
205
max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL);
206
if (max_lat) {
207
printf(" Max latency was %s\n", max_lat);
208
free(max_lat);
209
}
210
}
211
}
212
213
void timerlat_free(struct osnoise_tool *tool)
214
{
215
struct timerlat_params *params = to_timerlat_params(tool->params);
216
int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
217
int i;
218
219
timerlat_aa_destroy();
220
if (dma_latency_fd >= 0)
221
close(dma_latency_fd);
222
if (params->deepest_idle_state >= -1) {
223
for_each_monitored_cpu(i, nr_cpus, &params->common) {
224
restore_cpu_idle_disable_state(i);
225
}
226
}
227
228
osnoise_destroy_tool(tool->aa);
229
230
if (params->mode != TRACING_MODE_TRACEFS)
231
timerlat_bpf_destroy();
232
free_cpu_idle_disable_states();
233
}
234
235
static void timerlat_usage(int err)
236
{
237
int i;
238
239
static const char * const msg[] = {
240
"",
241
"timerlat version " VERSION,
242
"",
243
" usage: [rtla] timerlat [MODE] ...",
244
"",
245
" modes:",
246
" top - prints the summary from timerlat tracer",
247
" hist - prints a histogram of timer latencies",
248
"",
249
"if no MODE is given, the top mode is called, passing the arguments",
250
NULL,
251
};
252
253
for (i = 0; msg[i]; i++)
254
fprintf(stderr, "%s\n", msg[i]);
255
exit(err);
256
}
257
258
int timerlat_main(int argc, char *argv[])
259
{
260
if (argc == 0)
261
goto usage;
262
263
/*
264
* if timerlat was called without any argument, run the
265
* default cmdline.
266
*/
267
if (argc == 1) {
268
run_tool(&timerlat_top_ops, argc, argv);
269
exit(0);
270
}
271
272
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
273
timerlat_usage(0);
274
} else if (strncmp(argv[1], "-", 1) == 0) {
275
/* the user skipped the tool, call the default one */
276
run_tool(&timerlat_top_ops, argc, argv);
277
exit(0);
278
} else if (strcmp(argv[1], "top") == 0) {
279
run_tool(&timerlat_top_ops, argc-1, &argv[1]);
280
exit(0);
281
} else if (strcmp(argv[1], "hist") == 0) {
282
run_tool(&timerlat_hist_ops, argc-1, &argv[1]);
283
exit(0);
284
}
285
286
usage:
287
timerlat_usage(1);
288
exit(1);
289
}
290
291