xref: /linux/tools/tracing/rtla/src/common.c (revision 85cdaca6970028bf6f544c355c90035586836ddf)
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 
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  */
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  */
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
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, &params->monitored_cpus);
80 	}
81 
82 	if (params->hk_cpus) {
83 		retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
84 					   &params->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(&params->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
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 
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, &params->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 = &params->monitored_cpus;
238 		if (params->set_sched)
239 			params->user.sched_param = &params->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, &params->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(&params->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(&params->threshold_actions);
293 	actions_destroy(&params->end_actions);
294 	free(params);
295 out_exit:
296 	exit(return_value);
297 }
298 
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 
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 
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 
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  */
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