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