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