xref: /linux/tools/tracing/rtla/src/common.c (revision 115b06a008756ec777249dc9f432dc1a6c681396)
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 <getopt.h>
9 #include <sys/sysinfo.h>
10 
11 #include "common.h"
12 
13 struct trace_instance *trace_inst;
14 volatile int stop_tracing;
15 int nr_cpus;
16 
17 static void stop_trace(int sig)
18 {
19 	if (stop_tracing) {
20 		/*
21 		 * Stop requested twice in a row; abort event processing and
22 		 * exit immediately
23 		 */
24 		tracefs_iterate_stop(trace_inst->inst);
25 		return;
26 	}
27 	stop_tracing = 1;
28 	if (trace_inst)
29 		trace_instance_stop(trace_inst);
30 }
31 
32 /*
33  * set_signals - handles the signal to stop the tool
34  */
35 static void set_signals(struct common_params *params)
36 {
37 	signal(SIGINT, stop_trace);
38 	if (params->duration) {
39 		signal(SIGALRM, stop_trace);
40 		alarm(params->duration);
41 	}
42 }
43 
44 /*
45  * getopt_auto - auto-generates optstring from long_options
46  */
47 int getopt_auto(int argc, char **argv, const struct option *long_opts)
48 {
49 	char opts[256];
50 	int n = 0;
51 
52 	for (int i = 0; long_opts[i].name; i++) {
53 		if (long_opts[i].val < 32 || long_opts[i].val > 127)
54 			continue;
55 
56 		if (n + 4 >= sizeof(opts))
57 			fatal("optstring buffer overflow");
58 
59 		opts[n++] = long_opts[i].val;
60 
61 		if (long_opts[i].has_arg == required_argument)
62 			opts[n++] = ':';
63 		else if (long_opts[i].has_arg == optional_argument) {
64 			opts[n++] = ':';
65 			opts[n++] = ':';
66 		}
67 	}
68 
69 	opts[n] = '\0';
70 
71 	return getopt_long(argc, argv, opts, long_opts, NULL);
72 }
73 
74 /*
75  * common_parse_options - parse common command line options
76  *
77  * @argc: argument count
78  * @argv: argument vector
79  * @common: common parameters structure
80  *
81  * Parse command line options that are common to all rtla tools.
82  *
83  * Returns: non zero if a common option was parsed, or 0
84  * if the option should be handled by tool-specific parsing.
85  */
86 int common_parse_options(int argc, char **argv, struct common_params *common)
87 {
88 	struct trace_events *tevent;
89 	int saved_state = optind;
90 	int c;
91 
92 	static struct option long_options[] = {
93 		{"cpus",                required_argument,      0, 'c'},
94 		{"cgroup",              optional_argument,      0, 'C'},
95 		{"debug",               no_argument,            0, 'D'},
96 		{"duration",            required_argument,      0, 'd'},
97 		{"event",               required_argument,      0, 'e'},
98 		{"house-keeping",       required_argument,      0, 'H'},
99 		{"priority",            required_argument,      0, 'P'},
100 		{0, 0, 0, 0}
101 	};
102 
103 	opterr = 0;
104 	c = getopt_auto(argc, argv, long_options);
105 	opterr = 1;
106 
107 	switch (c) {
108 	case 'c':
109 		if (parse_cpu_set(optarg, &common->monitored_cpus))
110 			fatal("Invalid -c cpu list");
111 		common->cpus = optarg;
112 		break;
113 	case 'C':
114 		common->cgroup = 1;
115 		common->cgroup_name = parse_optional_arg(argc, argv);
116 		break;
117 	case 'D':
118 		config_debug = 1;
119 		break;
120 	case 'd':
121 		common->duration = parse_seconds_duration(optarg);
122 		if (!common->duration)
123 			fatal("Invalid -d duration");
124 		break;
125 	case 'e':
126 		tevent = trace_event_alloc(optarg);
127 		if (!tevent)
128 			fatal("Error alloc trace event");
129 
130 		if (common->events)
131 			tevent->next = common->events;
132 		common->events = tevent;
133 		break;
134 	case 'H':
135 		common->hk_cpus = 1;
136 		if (parse_cpu_set(optarg, &common->hk_cpu_set))
137 			fatal("Error parsing house keeping CPUs");
138 		break;
139 	case 'P':
140 		if (parse_prio(optarg, &common->sched_param) == -1)
141 			fatal("Invalid -P priority");
142 		common->set_sched = 1;
143 		break;
144 	default:
145 		optind = saved_state;
146 		return 0;
147 	}
148 
149 	return c;
150 }
151 
152 /*
153  * common_apply_config - apply common configs to the initialized tool
154  */
155 int
156 common_apply_config(struct osnoise_tool *tool, struct common_params *params)
157 {
158 	int retval, i;
159 
160 	if (!params->sleep_time)
161 		params->sleep_time = 1;
162 
163 	retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all");
164 	if (retval) {
165 		err_msg("Failed to apply CPUs config\n");
166 		goto out_err;
167 	}
168 
169 	if (!params->cpus) {
170 		for (i = 0; i < nr_cpus; i++)
171 			CPU_SET(i, &params->monitored_cpus);
172 	}
173 
174 	if (params->hk_cpus) {
175 		retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
176 					   &params->hk_cpu_set);
177 		if (retval == -1) {
178 			err_msg("Failed to set rtla to the house keeping CPUs\n");
179 			goto out_err;
180 		}
181 	} else if (params->cpus) {
182 		/*
183 		 * Even if the user do not set a house-keeping CPU, try to
184 		 * move rtla to a CPU set different to the one where the user
185 		 * set the workload to run.
186 		 *
187 		 * No need to check results as this is an automatic attempt.
188 		 */
189 		auto_house_keeping(&params->monitored_cpus);
190 	}
191 
192 	/*
193 	 * Set workload according to type of thread if the kernel supports it.
194 	 * On kernels without support, user threads will have already failed
195 	 * on missing fd, and kernel threads do not need it.
196 	 */
197 	retval = osnoise_set_workload(tool->context, params->kernel_workload);
198 	if (retval < -1) {
199 		err_msg("Failed to set OSNOISE_WORKLOAD option\n");
200 		goto out_err;
201 	}
202 
203 	return 0;
204 
205 out_err:
206 	return -1;
207 }
208 
209 
210 int run_tool(struct tool_ops *ops, int argc, char *argv[])
211 {
212 	struct common_params *params;
213 	enum result return_value = ERROR;
214 	struct osnoise_tool *tool;
215 	bool stopped;
216 	int retval;
217 
218 	nr_cpus = get_nprocs_conf();
219 	params = ops->parse_args(argc, argv);
220 	if (!params)
221 		exit(1);
222 
223 	tool = ops->init_tool(params);
224 	if (!tool) {
225 		err_msg("Could not init osnoise tool\n");
226 		goto out_exit;
227 	}
228 	tool->ops = ops;
229 	tool->params = params;
230 
231 	/*
232 	 * Save trace instance into global variable so that SIGINT can stop
233 	 * the timerlat tracer.
234 	 * Otherwise, rtla could loop indefinitely when overloaded.
235 	 */
236 	trace_inst = &tool->trace;
237 
238 	retval = ops->apply_config(tool);
239 	if (retval) {
240 		err_msg("Could not apply config\n");
241 		goto out_free;
242 	}
243 
244 	retval = enable_tracer_by_name(trace_inst->inst, ops->tracer);
245 	if (retval) {
246 		err_msg("Failed to enable %s tracer\n", ops->tracer);
247 		goto out_free;
248 	}
249 
250 	if (params->set_sched) {
251 		retval = set_comm_sched_attr(ops->comm_prefix, &params->sched_param);
252 		if (retval) {
253 			err_msg("Failed to set sched parameters\n");
254 			goto out_free;
255 		}
256 	}
257 
258 	if (params->cgroup && !params->user_data) {
259 		retval = set_comm_cgroup(ops->comm_prefix, params->cgroup_name);
260 		if (!retval) {
261 			err_msg("Failed to move threads to cgroup\n");
262 			goto out_free;
263 		}
264 	}
265 
266 
267 	if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] ||
268 	    params->end_actions.present[ACTION_TRACE_OUTPUT]) {
269 		tool->record = osnoise_init_trace_tool(ops->tracer);
270 		if (!tool->record) {
271 			err_msg("Failed to enable the trace instance\n");
272 			goto out_free;
273 		}
274 		params->threshold_actions.trace_output_inst = tool->record->trace.inst;
275 		params->end_actions.trace_output_inst = tool->record->trace.inst;
276 
277 		if (params->events) {
278 			retval = trace_events_enable(&tool->record->trace, params->events);
279 			if (retval)
280 				goto out_trace;
281 		}
282 
283 		if (params->buffer_size > 0) {
284 			retval = trace_set_buffer_size(&tool->record->trace, params->buffer_size);
285 			if (retval)
286 				goto out_trace;
287 		}
288 	}
289 
290 	if (params->user_workload) {
291 		pthread_t user_thread;
292 
293 		/* rtla asked to stop */
294 		params->user.should_run = 1;
295 		/* all threads left */
296 		params->user.stopped_running = 0;
297 
298 		params->user.set = &params->monitored_cpus;
299 		if (params->set_sched)
300 			params->user.sched_param = &params->sched_param;
301 		else
302 			params->user.sched_param = NULL;
303 
304 		params->user.cgroup_name = params->cgroup_name;
305 
306 		retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, &params->user);
307 		if (retval)
308 			err_msg("Error creating timerlat user-space threads\n");
309 	}
310 
311 	retval = ops->enable(tool);
312 	if (retval)
313 		goto out_trace;
314 
315 	tool->start_time = time(NULL);
316 	set_signals(params);
317 
318 	retval = ops->main(tool);
319 	if (retval)
320 		goto out_trace;
321 
322 	if (params->user_workload && !params->user.stopped_running) {
323 		params->user.should_run = 0;
324 		sleep(1);
325 	}
326 
327 	ops->print_stats(tool);
328 
329 	actions_perform(&params->end_actions);
330 
331 	return_value = PASSED;
332 
333 	stopped = osnoise_trace_is_off(tool, tool->record) && !stop_tracing;
334 	if (stopped) {
335 		printf("%s hit stop tracing\n", ops->tracer);
336 		return_value = FAILED;
337 	}
338 
339 	if (ops->analyze)
340 		ops->analyze(tool, stopped);
341 
342 out_trace:
343 	trace_events_destroy(&tool->record->trace, params->events);
344 	params->events = NULL;
345 out_free:
346 	ops->free(tool);
347 	osnoise_destroy_tool(tool->record);
348 	osnoise_destroy_tool(tool);
349 	actions_destroy(&params->threshold_actions);
350 	actions_destroy(&params->end_actions);
351 	free(params);
352 out_exit:
353 	exit(return_value);
354 }
355 
356 int top_main_loop(struct osnoise_tool *tool)
357 {
358 	struct common_params *params = tool->params;
359 	struct trace_instance *trace = &tool->trace;
360 	struct osnoise_tool *record = tool->record;
361 	int retval;
362 
363 	while (!stop_tracing) {
364 		sleep(params->sleep_time);
365 
366 		if (params->aa_only && !osnoise_trace_is_off(tool, record))
367 			continue;
368 
369 		retval = tracefs_iterate_raw_events(trace->tep,
370 						    trace->inst,
371 						    NULL,
372 						    0,
373 						    collect_registered_events,
374 						    trace);
375 		if (retval < 0) {
376 			err_msg("Error iterating on events\n");
377 			return retval;
378 		}
379 
380 		if (!params->quiet)
381 			tool->ops->print_stats(tool);
382 
383 		if (osnoise_trace_is_off(tool, record)) {
384 			if (stop_tracing)
385 				/* stop tracing requested, do not perform actions */
386 				return 0;
387 
388 			actions_perform(&params->threshold_actions);
389 
390 			if (!params->threshold_actions.continue_flag)
391 				/* continue flag not set, break */
392 				return 0;
393 
394 			/* continue action reached, re-enable tracing */
395 			if (record)
396 				trace_instance_start(&record->trace);
397 			if (tool->aa)
398 				trace_instance_start(&tool->aa->trace);
399 			trace_instance_start(trace);
400 		}
401 
402 		/* is there still any user-threads ? */
403 		if (params->user_workload) {
404 			if (params->user.stopped_running) {
405 				debug_msg("timerlat user space threads stopped!\n");
406 				break;
407 			}
408 		}
409 	}
410 
411 	return 0;
412 }
413 
414 int hist_main_loop(struct osnoise_tool *tool)
415 {
416 	struct common_params *params = tool->params;
417 	struct trace_instance *trace = &tool->trace;
418 	int retval = 0;
419 
420 	while (!stop_tracing) {
421 		sleep(params->sleep_time);
422 
423 		retval = tracefs_iterate_raw_events(trace->tep,
424 						    trace->inst,
425 						    NULL,
426 						    0,
427 						    collect_registered_events,
428 						    trace);
429 		if (retval < 0) {
430 			err_msg("Error iterating on events\n");
431 			break;
432 		}
433 
434 		if (osnoise_trace_is_off(tool, tool->record)) {
435 			if (stop_tracing)
436 				/* stop tracing requested, do not perform actions */
437 				break;
438 
439 			actions_perform(&params->threshold_actions);
440 
441 			if (!params->threshold_actions.continue_flag)
442 				/* continue flag not set, break */
443 				break;
444 
445 			/* continue action reached, re-enable tracing */
446 			if (tool->record)
447 				trace_instance_start(&tool->record->trace);
448 			if (tool->aa)
449 				trace_instance_start(&tool->aa->trace);
450 			trace_instance_start(&tool->trace);
451 		}
452 
453 		/* is there still any user-threads ? */
454 		if (params->user_workload) {
455 			if (params->user.stopped_running) {
456 				debug_msg("user-space threads stopped!\n");
457 				break;
458 			}
459 		}
460 	}
461 
462 	return retval;
463 }
464 
465 int osn_set_stop(struct osnoise_tool *tool)
466 {
467 	struct common_params *params = tool->params;
468 	int retval;
469 
470 	retval = osnoise_set_stop_us(tool->context, params->stop_us);
471 	if (retval) {
472 		err_msg("Failed to set stop us\n");
473 		return retval;
474 	}
475 
476 	retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
477 	if (retval) {
478 		err_msg("Failed to set stop total us\n");
479 		return retval;
480 	}
481 
482 	return 0;
483 }
484 
485 static void print_msg_array(const char * const *msgs)
486 {
487 	if (!msgs)
488 		return;
489 
490 	for (int i = 0; msgs[i]; i++)
491 		fprintf(stderr, "%s\n", msgs[i]);
492 }
493 
494 /*
495  * common_usage - print complete usage information
496  */
497 void common_usage(const char *tool, const char *mode,
498 		  const char *desc, const char * const *start_msgs, const char * const *opt_msgs)
499 {
500 	static const char * const common_options[] = {
501 		"	  -h/--help: print this menu",
502 		NULL
503 	};
504 	fprintf(stderr, "rtla %s", tool);
505 	if (strcmp(mode, ""))
506 		fprintf(stderr, " %s", mode);
507 	fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION);
508 	fprintf(stderr, "  usage: [rtla] %s ", tool);
509 
510 	if (strcmp(mode, "top") == 0)
511 		fprintf(stderr, "[top] [-h] ");
512 	else
513 		fprintf(stderr, "%s [-h] ", mode);
514 
515 	print_msg_array(start_msgs);
516 	fprintf(stderr, "\n");
517 	print_msg_array(common_options);
518 	print_msg_array(opt_msgs);
519 
520 	exit(EXIT_SUCCESS);
521 }
522