xref: /linux/tools/tracing/rtla/src/common.c (revision 5779de8d36ac5a0c929f276096a499b03ae0afa7)
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 			if (stop_tracing)
272 				/* stop tracing requested, do not perform actions */
273 				return 0;
274 
275 			actions_perform(&params->threshold_actions);
276 
277 			if (!params->threshold_actions.continue_flag)
278 				/* continue flag not set, break */
279 				return 0;
280 
281 			/* continue action reached, re-enable tracing */
282 			if (record)
283 				trace_instance_start(&record->trace);
284 			if (tool->aa)
285 				trace_instance_start(&tool->aa->trace);
286 			trace_instance_start(trace);
287 		}
288 
289 		/* is there still any user-threads ? */
290 		if (params->user_workload) {
291 			if (params->user.stopped_running) {
292 				debug_msg("timerlat user space threads stopped!\n");
293 				break;
294 			}
295 		}
296 	}
297 
298 	return 0;
299 }
300 
hist_main_loop(struct osnoise_tool * tool)301 int hist_main_loop(struct osnoise_tool *tool)
302 {
303 	struct common_params *params = tool->params;
304 	struct trace_instance *trace = &tool->trace;
305 	int retval = 0;
306 
307 	while (!stop_tracing) {
308 		sleep(params->sleep_time);
309 
310 		retval = tracefs_iterate_raw_events(trace->tep,
311 						    trace->inst,
312 						    NULL,
313 						    0,
314 						    collect_registered_events,
315 						    trace);
316 		if (retval < 0) {
317 			err_msg("Error iterating on events\n");
318 			break;
319 		}
320 
321 		if (osnoise_trace_is_off(tool, tool->record)) {
322 			if (stop_tracing)
323 				/* stop tracing requested, do not perform actions */
324 				break;
325 
326 			actions_perform(&params->threshold_actions);
327 
328 			if (!params->threshold_actions.continue_flag)
329 				/* continue flag not set, break */
330 				break;
331 
332 			/* continue action reached, re-enable tracing */
333 			if (tool->record)
334 				trace_instance_start(&tool->record->trace);
335 			if (tool->aa)
336 				trace_instance_start(&tool->aa->trace);
337 			trace_instance_start(&tool->trace);
338 		}
339 
340 		/* is there still any user-threads ? */
341 		if (params->user_workload) {
342 			if (params->user.stopped_running) {
343 				debug_msg("user-space threads stopped!\n");
344 				break;
345 			}
346 		}
347 	}
348 
349 	return retval;
350 }
351