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