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