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, ¶ms->monitored_cpus);
60 }
61
62 if (params->hk_cpus) {
63 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
64 ¶ms->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(¶ms->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, ¶ms->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 = ¶ms->monitored_cpus;
186 if (params->set_sched)
187 params->user.sched_param = ¶ms->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, ¶ms->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(¶ms->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(¶ms->threshold_actions);
237 actions_destroy(¶ms->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(¶ms->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(¶ms->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