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