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