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