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