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