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