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