1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3
4 #include <pthread.h>
5 #include <signal.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <getopt.h>
10 #include "common.h"
11
12 struct trace_instance *trace_inst;
13 volatile int stop_tracing;
14
stop_trace(int sig)15 static void stop_trace(int sig)
16 {
17 if (stop_tracing) {
18 /*
19 * Stop requested twice in a row; abort event processing and
20 * exit immediately
21 */
22 tracefs_iterate_stop(trace_inst->inst);
23 return;
24 }
25 stop_tracing = 1;
26 if (trace_inst)
27 trace_instance_stop(trace_inst);
28 }
29
30 /*
31 * set_signals - handles the signal to stop the tool
32 */
set_signals(struct common_params * params)33 static void set_signals(struct common_params *params)
34 {
35 signal(SIGINT, stop_trace);
36 if (params->duration) {
37 signal(SIGALRM, stop_trace);
38 alarm(params->duration);
39 }
40 }
41
42 /*
43 * common_parse_options - parse common command line options
44 *
45 * @argc: argument count
46 * @argv: argument vector
47 * @common: common parameters structure
48 *
49 * Parse command line options that are common to all rtla tools.
50 *
51 * Returns: non zero if a common option was parsed, or 0
52 * if the option should be handled by tool-specific parsing.
53 */
common_parse_options(int argc,char ** argv,struct common_params * common)54 int common_parse_options(int argc, char **argv, struct common_params *common)
55 {
56 struct trace_events *tevent;
57 int saved_state = optind;
58 int c;
59
60 static struct option long_options[] = {
61 {"cpus", required_argument, 0, 'c'},
62 {"cgroup", optional_argument, 0, 'C'},
63 {"debug", no_argument, 0, 'D'},
64 {"duration", required_argument, 0, 'd'},
65 {"event", required_argument, 0, 'e'},
66 {"house-keeping", required_argument, 0, 'H'},
67 {"priority", required_argument, 0, 'P'},
68 {0, 0, 0, 0}
69 };
70
71 opterr = 0;
72 c = getopt_long(argc, argv, "c:C::Dd:e:H:P:", long_options, NULL);
73 opterr = 1;
74
75 switch (c) {
76 case 'c':
77 if (parse_cpu_set(optarg, &common->monitored_cpus))
78 fatal("Invalid -c cpu list");
79 common->cpus = optarg;
80 break;
81 case 'C':
82 common->cgroup = 1;
83 common->cgroup_name = parse_optional_arg(argc, argv);
84 break;
85 case 'D':
86 config_debug = 1;
87 break;
88 case 'd':
89 common->duration = parse_seconds_duration(optarg);
90 if (!common->duration)
91 fatal("Invalid -d duration");
92 break;
93 case 'e':
94 tevent = trace_event_alloc(optarg);
95 if (!tevent)
96 fatal("Error alloc trace event");
97
98 if (common->events)
99 tevent->next = common->events;
100 common->events = tevent;
101 break;
102 case 'H':
103 common->hk_cpus = 1;
104 if (parse_cpu_set(optarg, &common->hk_cpu_set))
105 fatal("Error parsing house keeping CPUs");
106 break;
107 case 'P':
108 if (parse_prio(optarg, &common->sched_param) == -1)
109 fatal("Invalid -P priority");
110 common->set_sched = 1;
111 break;
112 default:
113 optind = saved_state;
114 return 0;
115 }
116
117 return c;
118 }
119
120 /*
121 * common_apply_config - apply common configs to the initialized tool
122 */
123 int
common_apply_config(struct osnoise_tool * tool,struct common_params * params)124 common_apply_config(struct osnoise_tool *tool, struct common_params *params)
125 {
126 int retval, i;
127
128 if (!params->sleep_time)
129 params->sleep_time = 1;
130
131 retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all");
132 if (retval) {
133 err_msg("Failed to apply CPUs config\n");
134 goto out_err;
135 }
136
137 if (!params->cpus) {
138 for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
139 CPU_SET(i, ¶ms->monitored_cpus);
140 }
141
142 if (params->hk_cpus) {
143 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
144 ¶ms->hk_cpu_set);
145 if (retval == -1) {
146 err_msg("Failed to set rtla to the house keeping CPUs\n");
147 goto out_err;
148 }
149 } else if (params->cpus) {
150 /*
151 * Even if the user do not set a house-keeping CPU, try to
152 * move rtla to a CPU set different to the one where the user
153 * set the workload to run.
154 *
155 * No need to check results as this is an automatic attempt.
156 */
157 auto_house_keeping(¶ms->monitored_cpus);
158 }
159
160 /*
161 * Set workload according to type of thread if the kernel supports it.
162 * On kernels without support, user threads will have already failed
163 * on missing fd, and kernel threads do not need it.
164 */
165 retval = osnoise_set_workload(tool->context, params->kernel_workload);
166 if (retval < -1) {
167 err_msg("Failed to set OSNOISE_WORKLOAD option\n");
168 goto out_err;
169 }
170
171 return 0;
172
173 out_err:
174 return -1;
175 }
176
177
run_tool(struct tool_ops * ops,int argc,char * argv[])178 int run_tool(struct tool_ops *ops, int argc, char *argv[])
179 {
180 struct common_params *params;
181 enum result return_value = ERROR;
182 struct osnoise_tool *tool;
183 bool stopped;
184 int retval;
185
186 params = ops->parse_args(argc, argv);
187 if (!params)
188 exit(1);
189
190 tool = ops->init_tool(params);
191 if (!tool) {
192 err_msg("Could not init osnoise tool\n");
193 goto out_exit;
194 }
195 tool->ops = ops;
196 tool->params = params;
197
198 /*
199 * Save trace instance into global variable so that SIGINT can stop
200 * the timerlat tracer.
201 * Otherwise, rtla could loop indefinitely when overloaded.
202 */
203 trace_inst = &tool->trace;
204
205 retval = ops->apply_config(tool);
206 if (retval) {
207 err_msg("Could not apply config\n");
208 goto out_free;
209 }
210
211 retval = enable_tracer_by_name(trace_inst->inst, ops->tracer);
212 if (retval) {
213 err_msg("Failed to enable %s tracer\n", ops->tracer);
214 goto out_free;
215 }
216
217 if (params->set_sched) {
218 retval = set_comm_sched_attr(ops->comm_prefix, ¶ms->sched_param);
219 if (retval) {
220 err_msg("Failed to set sched parameters\n");
221 goto out_free;
222 }
223 }
224
225 if (params->cgroup && !params->user_data) {
226 retval = set_comm_cgroup(ops->comm_prefix, params->cgroup_name);
227 if (!retval) {
228 err_msg("Failed to move threads to cgroup\n");
229 goto out_free;
230 }
231 }
232
233
234 if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] ||
235 params->end_actions.present[ACTION_TRACE_OUTPUT]) {
236 tool->record = osnoise_init_trace_tool(ops->tracer);
237 if (!tool->record) {
238 err_msg("Failed to enable the trace instance\n");
239 goto out_free;
240 }
241 params->threshold_actions.trace_output_inst = tool->record->trace.inst;
242 params->end_actions.trace_output_inst = tool->record->trace.inst;
243
244 if (params->events) {
245 retval = trace_events_enable(&tool->record->trace, params->events);
246 if (retval)
247 goto out_trace;
248 }
249
250 if (params->buffer_size > 0) {
251 retval = trace_set_buffer_size(&tool->record->trace, params->buffer_size);
252 if (retval)
253 goto out_trace;
254 }
255 }
256
257 if (params->user_workload) {
258 pthread_t user_thread;
259
260 /* rtla asked to stop */
261 params->user.should_run = 1;
262 /* all threads left */
263 params->user.stopped_running = 0;
264
265 params->user.set = ¶ms->monitored_cpus;
266 if (params->set_sched)
267 params->user.sched_param = ¶ms->sched_param;
268 else
269 params->user.sched_param = NULL;
270
271 params->user.cgroup_name = params->cgroup_name;
272
273 retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, ¶ms->user);
274 if (retval)
275 err_msg("Error creating timerlat user-space threads\n");
276 }
277
278 retval = ops->enable(tool);
279 if (retval)
280 goto out_trace;
281
282 tool->start_time = time(NULL);
283 set_signals(params);
284
285 retval = ops->main(tool);
286 if (retval)
287 goto out_trace;
288
289 if (params->user_workload && !params->user.stopped_running) {
290 params->user.should_run = 0;
291 sleep(1);
292 }
293
294 ops->print_stats(tool);
295
296 actions_perform(¶ms->end_actions);
297
298 return_value = PASSED;
299
300 stopped = osnoise_trace_is_off(tool, tool->record) && !stop_tracing;
301 if (stopped) {
302 printf("%s hit stop tracing\n", ops->tracer);
303 return_value = FAILED;
304 }
305
306 if (ops->analyze)
307 ops->analyze(tool, stopped);
308
309 out_trace:
310 trace_events_destroy(&tool->record->trace, params->events);
311 params->events = NULL;
312 out_free:
313 ops->free(tool);
314 osnoise_destroy_tool(tool->record);
315 osnoise_destroy_tool(tool);
316 actions_destroy(¶ms->threshold_actions);
317 actions_destroy(¶ms->end_actions);
318 free(params);
319 out_exit:
320 exit(return_value);
321 }
322
top_main_loop(struct osnoise_tool * tool)323 int top_main_loop(struct osnoise_tool *tool)
324 {
325 struct common_params *params = tool->params;
326 struct trace_instance *trace = &tool->trace;
327 struct osnoise_tool *record = tool->record;
328 int retval;
329
330 while (!stop_tracing) {
331 sleep(params->sleep_time);
332
333 if (params->aa_only && !osnoise_trace_is_off(tool, record))
334 continue;
335
336 retval = tracefs_iterate_raw_events(trace->tep,
337 trace->inst,
338 NULL,
339 0,
340 collect_registered_events,
341 trace);
342 if (retval < 0) {
343 err_msg("Error iterating on events\n");
344 return retval;
345 }
346
347 if (!params->quiet)
348 tool->ops->print_stats(tool);
349
350 if (osnoise_trace_is_off(tool, record)) {
351 if (stop_tracing)
352 /* stop tracing requested, do not perform actions */
353 return 0;
354
355 actions_perform(¶ms->threshold_actions);
356
357 if (!params->threshold_actions.continue_flag)
358 /* continue flag not set, break */
359 return 0;
360
361 /* continue action reached, re-enable tracing */
362 if (record)
363 trace_instance_start(&record->trace);
364 if (tool->aa)
365 trace_instance_start(&tool->aa->trace);
366 trace_instance_start(trace);
367 }
368
369 /* is there still any user-threads ? */
370 if (params->user_workload) {
371 if (params->user.stopped_running) {
372 debug_msg("timerlat user space threads stopped!\n");
373 break;
374 }
375 }
376 }
377
378 return 0;
379 }
380
hist_main_loop(struct osnoise_tool * tool)381 int hist_main_loop(struct osnoise_tool *tool)
382 {
383 struct common_params *params = tool->params;
384 struct trace_instance *trace = &tool->trace;
385 int retval = 0;
386
387 while (!stop_tracing) {
388 sleep(params->sleep_time);
389
390 retval = tracefs_iterate_raw_events(trace->tep,
391 trace->inst,
392 NULL,
393 0,
394 collect_registered_events,
395 trace);
396 if (retval < 0) {
397 err_msg("Error iterating on events\n");
398 break;
399 }
400
401 if (osnoise_trace_is_off(tool, tool->record)) {
402 if (stop_tracing)
403 /* stop tracing requested, do not perform actions */
404 break;
405
406 actions_perform(¶ms->threshold_actions);
407
408 if (!params->threshold_actions.continue_flag)
409 /* continue flag not set, break */
410 break;
411
412 /* continue action reached, re-enable tracing */
413 if (tool->record)
414 trace_instance_start(&tool->record->trace);
415 if (tool->aa)
416 trace_instance_start(&tool->aa->trace);
417 trace_instance_start(&tool->trace);
418 }
419
420 /* is there still any user-threads ? */
421 if (params->user_workload) {
422 if (params->user.stopped_running) {
423 debug_msg("user-space threads stopped!\n");
424 break;
425 }
426 }
427 }
428
429 return retval;
430 }
431
osn_set_stop(struct osnoise_tool * tool)432 int osn_set_stop(struct osnoise_tool *tool)
433 {
434 struct common_params *params = tool->params;
435 int retval;
436
437 retval = osnoise_set_stop_us(tool->context, params->stop_us);
438 if (retval) {
439 err_msg("Failed to set stop us\n");
440 return retval;
441 }
442
443 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
444 if (retval) {
445 err_msg("Failed to set stop total us\n");
446 return retval;
447 }
448
449 return 0;
450 }
451
print_msg_array(const char * const * msgs)452 static void print_msg_array(const char * const *msgs)
453 {
454 if (!msgs)
455 return;
456
457 for (int i = 0; msgs[i]; i++)
458 fprintf(stderr, "%s\n", msgs[i]);
459 }
460
461 /*
462 * common_usage - print complete usage information
463 */
common_usage(const char * tool,const char * mode,const char * desc,const char * const * start_msgs,const char * const * opt_msgs)464 void common_usage(const char *tool, const char *mode,
465 const char *desc, const char * const *start_msgs, const char * const *opt_msgs)
466 {
467 static const char * const common_options[] = {
468 " -h/--help: print this menu",
469 NULL
470 };
471 fprintf(stderr, "rtla %s", tool);
472 if (strcmp(mode, ""))
473 fprintf(stderr, " %s", mode);
474 fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION);
475 fprintf(stderr, " usage: [rtla] %s ", tool);
476
477 if (strcmp(mode, "top") == 0)
478 fprintf(stderr, "[top] [-h] ");
479 else
480 fprintf(stderr, "%s [-h] ", mode);
481
482 print_msg_array(start_msgs);
483 fprintf(stderr, "\n");
484 print_msg_array(common_options);
485 print_msg_array(opt_msgs);
486
487 exit(EXIT_SUCCESS);
488 }
489