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