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