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