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