forked from qais-yousef/sched-analyzer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse_argp.c
312 lines (297 loc) · 8.92 KB
/
parse_argp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2023 Qais Yousef */
#include <stdlib.h>
#include <string.h>
#include "parse_argp.h"
#define XSTR(x) STR(x)
#define STR(x) #x
const char *argp_program_version = "sched-analyzer " XSTR(SA_VERSION);
const char *argp_program_bug_address = "<[email protected]>";
static char doc[] =
"Extract scheduer data using BPF and emit them into perfetto as track events";
struct sa_opts sa_opts = {
/* perfetto opts */
.system = true,
.app = false,
/* controls */
.output = "sched-analyzer.perfetto-trace",
.output_path = NULL,
.max_size = 250 * 1024 * 1024, /* 250MiB */
.num_ftrace_event = 0,
.num_atrace_cat = 0,
.num_function_graph = 0,
.num_function_filter = 0,
.ftrace_event = { 0 },
.atrace_cat = { 0 },
.function_graph = { 0 },
.function_filter = { 0 },
/* events */
.load_avg_cpu = false,
.runnable_avg_cpu = false,
.util_avg_cpu = false,
.load_avg_task = false,
.runnable_avg_task = false,
.util_avg_cpu = false,
.util_avg_task = false,
.util_avg_rt = false,
.util_avg_dl = false,
.util_avg_irq = false,
.load_avg_thermal = false,
.util_est_cpu = false,
.util_est_task = false,
.cpu_nr_running = false,
.cpu_freq = false,
.cpu_idle = false,
.softirq = false,
.sched_switch = false,
.load_balance = false,
.ipi = false,
.irq = false,
/* filters */
.num_pids = 0,
.num_comms = 0,
.pid = { 0 },
.comm = { { 0 } },
};
enum sa_opts_flags {
OPT_DUMMY_START = 0x80,
/* perfetto opts */
OPT_SYSTEM,
OPT_APP,
/* controls */
OPT_OUTPUT,
OPT_OUTPUT_PATH,
OPT_MAX_SIZE,
OPT_FTRACE_EVENT,
OPT_ATRACE_CAT,
OPT_FUNCTION_GRAPH,
OPT_FUNCTION_FILTER,
/* events */
OPT_LOAD_AVG,
OPT_RUNNABLE_AVG,
OPT_UTIL_AVG,
OPT_LOAD_AVG_CPU,
OPT_RUNNABLE_AVG_CPU,
OPT_UTIL_AVG_CPU,
OPT_LOAD_AVG_TASK,
OPT_RUNNABLE_AVG_TASK,
OPT_UTIL_AVG_TASK,
OPT_UTIL_AVG_RT,
OPT_UTIL_AVG_DL,
OPT_UTIL_AVG_IRQ,
OPT_LOAD_AVG_THERMAL,
OPT_UTIL_EST,
OPT_UTIL_EST_CPU,
OPT_UTIL_EST_TASK,
OPT_CPU_NR_RUNNING,
OPT_CPU_IDLE,
OPT_LOAD_BALANCE,
OPT_IPI,
OPT_IRQ,
/* filters */
OPT_FILTER_PID,
OPT_FILTER_COMM,
};
static const struct argp_option options[] = {
/* perfetto opts */
{ "system", OPT_SYSTEM, 0, 0, "Collect system wide data, requires traced and traced_probes to be running (default)." },
{ "app", OPT_APP, 0, 0, "Collect only data generated by this app. Runs standalone without external dependencies on traced." },
/* controls */
{ "output", OPT_OUTPUT, "FILE", 0, "Filename of the perfetto-trace file to produce." },
{ "output_path", OPT_OUTPUT_PATH, "PATH", 0, "Path to store perfetto-trace. PWD by default for perfetto." },
{ "max_size", OPT_MAX_SIZE, "SIZE(MiB)", 0, "Maximum size of perfetto file to produce, 250MiB by default." },
{ "ftrace_event", OPT_FTRACE_EVENT, "FTRACE_EVENT", 0, "Add ftrace event to the captured data. Repeat for each event to add." },
/* events */
{ "atrace_cat", OPT_ATRACE_CAT, "ATRACE_CATEGORY", 0, "Perfetto atrace category to add to perfetto config. Repeat for each category to add." },
{ "function_graph", OPT_FUNCTION_GRAPH, "FUNCTION", 0, "Trace function call graph for a kernel FUNCTION. Based on ftrace function graph functionality. Repeat for each function to graph." },
{ "function_filter", OPT_FUNCTION_FILTER, "FUNCTION", 0, "Filter the function call for a kernel FUNCTION. Based on ftrace function filter functionality. Repeat for each function to filter." },
/* events */
{ "load_avg", OPT_LOAD_AVG, 0, 0, "Collect load_avg for CPU, tasks and thermal." },
{ "runnable_avg", OPT_RUNNABLE_AVG, 0, 0, "Collect runnable_avg for CPU and tasks." },
{ "util_avg", OPT_UTIL_AVG, 0, 0, "Collect util_avg for CPU, tasks, irq, dl and rt." },
{ "load_avg_cpu", OPT_LOAD_AVG_CPU, 0, 0, "Collect load_avg for CPU." },
{ "runnable_avg_cpu", OPT_RUNNABLE_AVG_CPU, 0, 0, "Collect runnable_avg for CPU." },
{ "util_avg_cpu", OPT_UTIL_AVG_CPU, 0, 0, "Collect util_avg for CPU." },
{ "load_avg_task", OPT_LOAD_AVG_TASK, 0, 0, "Collect load_avg for tasks." },
{ "runnable_avg_task", OPT_RUNNABLE_AVG_TASK, 0, 0, "Collect runnable_avg for tasks." },
{ "util_avg_task", OPT_UTIL_AVG_TASK, 0, 0, "Collect util_avg for tasks." },
{ "util_avg_rt", OPT_UTIL_AVG_RT, 0, 0, "Collect util_avg for rt." },
{ "util_avg_dl", OPT_UTIL_AVG_DL, 0, 0, "Collect util_avg for dl." },
{ "util_avg_irq", OPT_UTIL_AVG_IRQ, 0, 0, "Collect util_avg for irq." },
{ "load_avg_thermal", OPT_LOAD_AVG_THERMAL, 0, 0, "Collect load_avg for thermal pressure." },
{ "util_est", OPT_UTIL_EST, 0, 0, "Collect util_est for CPU and tasks." },
{ "util_est_cpu", OPT_UTIL_EST_CPU, 0, 0, "Collect util_est for CPU." },
{ "util_est_task", OPT_UTIL_EST_TASK, 0, 0, "Collect util_est for tasks." },
{ "cpu_nr_running", OPT_CPU_NR_RUNNING, 0, 0, "Collect nr_running tasks for each CPU." },
{ "cpu_idle", OPT_CPU_IDLE, 0, 0, "Collect info about cpu idle states for each CPU." },
{ "load_balance", OPT_LOAD_BALANCE, 0, 0, "Collect load balance related info." },
{ "ipi", OPT_IPI, 0, 0, "Collect ipi related info." },
{ "irq", OPT_IRQ, 0, 0, "Enable perfetto irq atrace category." },
/* filters */
{ "pid", OPT_FILTER_PID, "PID", 0, "Collect data for task match pid only. Can be provided multiple times." },
{ "comm", OPT_FILTER_COMM, "COMM", 0, "Collect data for tasks that contain comm only. Can be provided multiple times." },
{ 0 },
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
char *end_ptr;
switch (key) {
/* perfetto opts */
case OPT_SYSTEM:
sa_opts.system = true;
sa_opts.app = false;
break;
case OPT_APP:
sa_opts.system = false;
sa_opts.app = true;
break;
/* controls */
case OPT_OUTPUT:
sa_opts.output = arg;
break;
case OPT_OUTPUT_PATH:
sa_opts.output_path = arg;
break;
case OPT_MAX_SIZE:
errno = 0;
sa_opts.max_size = strtol(arg, &end_ptr, 0) * 1024 * 1024;
if (errno != 0) {
perror("Unsupported max_size value\n");
return errno;
}
if (end_ptr == arg) {
fprintf(stderr, "max_size: no digits were found\n");
argp_usage(state);
return -EINVAL;
}
break;
case OPT_FTRACE_EVENT:
sa_opts.ftrace_event[sa_opts.num_ftrace_event] = arg;
sa_opts.num_ftrace_event++;
break;
case OPT_ATRACE_CAT:
sa_opts.atrace_cat[sa_opts.num_atrace_cat] = arg;
sa_opts.num_atrace_cat++;
break;
case OPT_FUNCTION_GRAPH:
sa_opts.function_graph[sa_opts.num_function_graph] = arg;
sa_opts.num_function_graph++;
break;
case OPT_FUNCTION_FILTER:
sa_opts.function_filter[sa_opts.num_function_filter] = arg;
sa_opts.num_function_filter++;
break;
/* events */
case OPT_LOAD_AVG:
sa_opts.load_avg_cpu = true;
sa_opts.load_avg_task = true;
sa_opts.load_avg_thermal = true;
break;
case OPT_RUNNABLE_AVG:
sa_opts.runnable_avg_cpu = true;
sa_opts.runnable_avg_task = true;
break;
case OPT_UTIL_AVG:
sa_opts.util_avg_cpu = true;
sa_opts.util_avg_task = true;
sa_opts.util_avg_rt = true;
sa_opts.util_avg_dl = true;
sa_opts.util_avg_irq = true;
break;
case OPT_LOAD_AVG_CPU:
sa_opts.load_avg_cpu = true;
break;
case OPT_RUNNABLE_AVG_CPU:
sa_opts.runnable_avg_cpu = true;
break;
case OPT_UTIL_AVG_CPU:
sa_opts.util_avg_cpu = true;
break;
case OPT_LOAD_AVG_TASK:
sa_opts.load_avg_task = true;
break;
case OPT_RUNNABLE_AVG_TASK:
sa_opts.runnable_avg_task = true;
break;
case OPT_UTIL_AVG_TASK:
sa_opts.util_avg_task = true;
break;
case OPT_UTIL_AVG_RT:
sa_opts.util_avg_rt = true;
break;
case OPT_UTIL_AVG_DL:
sa_opts.util_avg_dl = true;
break;
case OPT_UTIL_AVG_IRQ:
sa_opts.util_avg_irq = true;
break;
case OPT_LOAD_AVG_THERMAL:
sa_opts.load_avg_thermal = true;
break;
case OPT_UTIL_EST:
sa_opts.util_est_cpu = true;
sa_opts.util_est_task = true;
break;
case OPT_UTIL_EST_CPU:
sa_opts.util_est_cpu = true;
break;
case OPT_UTIL_EST_TASK:
sa_opts.util_est_task = true;
break;
case OPT_CPU_NR_RUNNING:
sa_opts.cpu_nr_running = true;
break;
case OPT_CPU_IDLE:
sa_opts.cpu_idle = true;
break;
case OPT_LOAD_BALANCE:
sa_opts.load_balance = true;
break;
case OPT_IPI:
sa_opts.ipi = true;
break;
case OPT_IRQ:
sa_opts.irq = true;
break;
case OPT_FILTER_PID:
if (sa_opts.num_pids >= MAX_FILTERS_NUM) {
fprintf(stderr, "Can't accept more --pid, dropping %s\n", arg);
break;
}
errno = 0;
sa_opts.pid[sa_opts.num_pids] = strtol(arg, &end_ptr, 0);
if (errno != 0) {
perror("Unsupported pid value\n");
return errno;
}
if (end_ptr == arg) {
fprintf(stderr, "pid: no digits were found\n");
argp_usage(state);
return -EINVAL;
}
sa_opts.num_pids++;
break;
case OPT_FILTER_COMM:
if (sa_opts.num_comms >= MAX_FILTERS_NUM) {
fprintf(stderr, "Can't accept more --comm, dropping %s\n", arg);
break;
}
strncpy(sa_opts.comm[sa_opts.num_comms], arg, TASK_COMM_LEN-1);
sa_opts.comm[sa_opts.num_comms][TASK_COMM_LEN-1] = 0;
sa_opts.num_comms++;
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp argp = {
.options = options,
.parser = parse_arg,
.doc = doc,
};