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