xref: /openbmc/linux/tools/tracing/rtla/src/trace.c (revision bc33f5e5)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <sys/sendfile.h>
4 #include <tracefs.h>
5 #include <signal.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <errno.h>
9 
10 #include "trace.h"
11 #include "utils.h"
12 
13 /*
14  * enable_tracer_by_name - enable a tracer on the given instance
15  */
16 int enable_tracer_by_name(struct tracefs_instance *inst, const char *tracer_name)
17 {
18 	enum tracefs_tracers tracer;
19 	int retval;
20 
21 	tracer = TRACEFS_TRACER_CUSTOM;
22 
23 	debug_msg("Enabling %s tracer\n", tracer_name);
24 
25 	retval = tracefs_tracer_set(inst, tracer, tracer_name);
26 	if (retval < 0) {
27 		if (errno == ENODEV)
28 			err_msg("Tracer %s not found!\n", tracer_name);
29 
30 		err_msg("Failed to enable the %s tracer\n", tracer_name);
31 		return -1;
32 	}
33 
34 	return 0;
35 }
36 
37 /*
38  * disable_tracer - set nop tracer to the insta
39  */
40 void disable_tracer(struct tracefs_instance *inst)
41 {
42 	enum tracefs_tracers t = TRACEFS_TRACER_NOP;
43 	int retval;
44 
45 	retval = tracefs_tracer_set(inst, t);
46 	if (retval < 0)
47 		err_msg("Oops, error disabling tracer\n");
48 }
49 
50 /*
51  * create_instance - create a trace instance with *instance_name
52  */
53 struct tracefs_instance *create_instance(char *instance_name)
54 {
55 	return tracefs_instance_create(instance_name);
56 }
57 
58 /*
59  * destroy_instance - remove a trace instance and free the data
60  */
61 void destroy_instance(struct tracefs_instance *inst)
62 {
63 	tracefs_instance_destroy(inst);
64 	tracefs_instance_free(inst);
65 }
66 
67 /*
68  * save_trace_to_file - save the trace output of the instance to the file
69  */
70 int save_trace_to_file(struct tracefs_instance *inst, const char *filename)
71 {
72 	const char *file = "trace";
73 	mode_t mode = 0644;
74 	char buffer[4096];
75 	int out_fd, in_fd;
76 	int retval = -1;
77 
78 	in_fd = tracefs_instance_file_open(inst, file, O_RDONLY);
79 	if (in_fd < 0) {
80 		err_msg("Failed to open trace file\n");
81 		return -1;
82 	}
83 
84 	out_fd = creat(filename, mode);
85 	if (out_fd < 0) {
86 		err_msg("Failed to create output file %s\n", filename);
87 		goto out_close_in;
88 	}
89 
90 	do {
91 		retval = read(in_fd, buffer, sizeof(buffer));
92 		if (retval <= 0)
93 			goto out_close;
94 
95 		retval = write(out_fd, buffer, retval);
96 		if (retval < 0)
97 			goto out_close;
98 	} while (retval > 0);
99 
100 	retval = 0;
101 out_close:
102 	close(out_fd);
103 out_close_in:
104 	close(in_fd);
105 	return retval;
106 }
107 
108 /*
109  * collect_registered_events - call the existing callback function for the event
110  *
111  * If an event has a registered callback function, call it.
112  * Otherwise, ignore the event.
113  */
114 int
115 collect_registered_events(struct tep_event *event, struct tep_record *record,
116 			  int cpu, void *context)
117 {
118 	struct trace_instance *trace = context;
119 	struct trace_seq *s = trace->seq;
120 
121 	if (!event->handler)
122 		return 0;
123 
124 	event->handler(s, record, event, context);
125 
126 	return 0;
127 }
128 
129 /*
130  * trace_instance_destroy - destroy and free a rtla trace instance
131  */
132 void trace_instance_destroy(struct trace_instance *trace)
133 {
134 	if (trace->inst) {
135 		disable_tracer(trace->inst);
136 		destroy_instance(trace->inst);
137 		trace->inst = NULL;
138 	}
139 
140 	if (trace->seq) {
141 		free(trace->seq);
142 		trace->seq = NULL;
143 	}
144 
145 	if (trace->tep) {
146 		tep_free(trace->tep);
147 		trace->tep = NULL;
148 	}
149 }
150 
151 /*
152  * trace_instance_init - create an rtla trace instance
153  *
154  * It is more than the tracefs instance, as it contains other
155  * things required for the tracing, such as the local events and
156  * a seq file.
157  *
158  * Note that the trace instance is returned disabled. This allows
159  * the tool to apply some other configs, like setting priority
160  * to the kernel threads, before starting generating trace entries.
161  */
162 int trace_instance_init(struct trace_instance *trace, char *tool_name)
163 {
164 	trace->seq = calloc(1, sizeof(*trace->seq));
165 	if (!trace->seq)
166 		goto out_err;
167 
168 	trace_seq_init(trace->seq);
169 
170 	trace->inst = create_instance(tool_name);
171 	if (!trace->inst)
172 		goto out_err;
173 
174 	trace->tep = tracefs_local_events(NULL);
175 	if (!trace->tep)
176 		goto out_err;
177 
178 	/*
179 	 * Let the main enable the record after setting some other
180 	 * things such as the priority of the tracer's threads.
181 	 */
182 	tracefs_trace_off(trace->inst);
183 
184 	return 0;
185 
186 out_err:
187 	trace_instance_destroy(trace);
188 	return 1;
189 }
190 
191 /*
192  * trace_instance_start - start tracing a given rtla instance
193  */
194 int trace_instance_start(struct trace_instance *trace)
195 {
196 	return tracefs_trace_on(trace->inst);
197 }
198 
199 /*
200  * trace_events_free - free a list of trace events
201  */
202 static void trace_events_free(struct trace_events *events)
203 {
204 	struct trace_events *tevent = events;
205 	struct trace_events *free_event;
206 
207 	while (tevent) {
208 		free_event = tevent;
209 
210 		tevent = tevent->next;
211 
212 		if (free_event->filter)
213 			free(free_event->filter);
214 		if (free_event->trigger)
215 			free(free_event->trigger);
216 		free(free_event->system);
217 		free(free_event);
218 	}
219 }
220 
221 /*
222  * trace_event_alloc - alloc and parse a single trace event
223  */
224 struct trace_events *trace_event_alloc(const char *event_string)
225 {
226 	struct trace_events *tevent;
227 
228 	tevent = calloc(1, sizeof(*tevent));
229 	if (!tevent)
230 		return NULL;
231 
232 	tevent->system = strdup(event_string);
233 	if (!tevent->system) {
234 		free(tevent);
235 		return NULL;
236 	}
237 
238 	tevent->event = strstr(tevent->system, ":");
239 	if (tevent->event) {
240 		*tevent->event = '\0';
241 		tevent->event = &tevent->event[1];
242 	}
243 
244 	return tevent;
245 }
246 
247 /*
248  * trace_event_add_filter - record an event filter
249  */
250 int trace_event_add_filter(struct trace_events *event, char *filter)
251 {
252 	if (event->filter)
253 		free(event->filter);
254 
255 	event->filter = strdup(filter);
256 	if (!event->filter)
257 		return 1;
258 
259 	return 0;
260 }
261 
262 /*
263  * trace_event_add_trigger - record an event trigger action
264  */
265 int trace_event_add_trigger(struct trace_events *event, char *trigger)
266 {
267 	if (event->trigger)
268 		free(event->trigger);
269 
270 	event->trigger = strdup(trigger);
271 	if (!event->trigger)
272 		return 1;
273 
274 	return 0;
275 }
276 
277 /*
278  * trace_event_disable_filter - disable an event filter
279  */
280 static void trace_event_disable_filter(struct trace_instance *instance,
281 				       struct trace_events *tevent)
282 {
283 	char filter[1024];
284 	int retval;
285 
286 	if (!tevent->filter)
287 		return;
288 
289 	if (!tevent->filter_enabled)
290 		return;
291 
292 	debug_msg("Disabling %s:%s filter %s\n", tevent->system,
293 		  tevent->event ? : "*", tevent->filter);
294 
295 	snprintf(filter, 1024, "!%s\n", tevent->filter);
296 
297 	retval = tracefs_event_file_write(instance->inst, tevent->system,
298 					  tevent->event, "filter", filter);
299 	if (retval < 0)
300 		err_msg("Error disabling %s:%s filter %s\n", tevent->system,
301 			tevent->event ? : "*", tevent->filter);
302 }
303 
304 /*
305  * trace_event_save_hist - save the content of an event hist
306  *
307  * If the trigger is a hist: one, save the content of the hist file.
308  */
309 static void trace_event_save_hist(struct trace_instance *instance,
310 				  struct trace_events *tevent)
311 {
312 	int retval, index, out_fd;
313 	mode_t mode = 0644;
314 	char path[1024];
315 	char *hist;
316 
317 	if (!tevent)
318 		return;
319 
320 	/* trigger enables hist */
321 	if (!tevent->trigger)
322 		return;
323 
324 	/* is this a hist: trigger? */
325 	retval = strncmp(tevent->trigger, "hist:", strlen("hist:"));
326 	if (retval)
327 		return;
328 
329 	snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event);
330 
331 	printf("  Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path);
332 
333 	out_fd = creat(path, mode);
334 	if (out_fd < 0) {
335 		err_msg("  Failed to create %s output file\n", path);
336 		return;
337 	}
338 
339 	hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0);
340 	if (!hist) {
341 		err_msg("  Failed to read %s:%s hist file\n", tevent->system, tevent->event);
342 		goto out_close;
343 	}
344 
345 	index = 0;
346 	do {
347 		index += write(out_fd, &hist[index], strlen(hist) - index);
348 	} while (index < strlen(hist));
349 
350 	free(hist);
351 out_close:
352 	close(out_fd);
353 }
354 
355 /*
356  * trace_event_disable_trigger - disable an event trigger
357  */
358 static void trace_event_disable_trigger(struct trace_instance *instance,
359 					struct trace_events *tevent)
360 {
361 	char trigger[1024];
362 	int retval;
363 
364 	if (!tevent->trigger)
365 		return;
366 
367 	if (!tevent->trigger_enabled)
368 		return;
369 
370 	debug_msg("Disabling %s:%s trigger %s\n", tevent->system,
371 		  tevent->event ? : "*", tevent->trigger);
372 
373 	trace_event_save_hist(instance, tevent);
374 
375 	snprintf(trigger, 1024, "!%s\n", tevent->trigger);
376 
377 	retval = tracefs_event_file_write(instance->inst, tevent->system,
378 					  tevent->event, "trigger", trigger);
379 	if (retval < 0)
380 		err_msg("Error disabling %s:%s trigger %s\n", tevent->system,
381 			tevent->event ? : "*", tevent->trigger);
382 }
383 
384 /*
385  * trace_events_disable - disable all trace events
386  */
387 void trace_events_disable(struct trace_instance *instance,
388 			  struct trace_events *events)
389 {
390 	struct trace_events *tevent = events;
391 
392 	if (!events)
393 		return;
394 
395 	while (tevent) {
396 		debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*");
397 		if (tevent->enabled) {
398 			trace_event_disable_filter(instance, tevent);
399 			trace_event_disable_trigger(instance, tevent);
400 			tracefs_event_disable(instance->inst, tevent->system, tevent->event);
401 		}
402 
403 		tevent->enabled = 0;
404 		tevent = tevent->next;
405 	}
406 }
407 
408 /*
409  * trace_event_enable_filter - enable an event filter associated with an event
410  */
411 static int trace_event_enable_filter(struct trace_instance *instance,
412 				     struct trace_events *tevent)
413 {
414 	char filter[1024];
415 	int retval;
416 
417 	if (!tevent->filter)
418 		return 0;
419 
420 	if (!tevent->event) {
421 		err_msg("Filter %s applies only for single events, not for all %s:* events\n",
422 			tevent->filter, tevent->system);
423 		return 1;
424 	}
425 
426 	snprintf(filter, 1024, "%s\n", tevent->filter);
427 
428 	debug_msg("Enabling %s:%s filter %s\n", tevent->system,
429 		  tevent->event ? : "*", tevent->filter);
430 
431 	retval = tracefs_event_file_write(instance->inst, tevent->system,
432 					  tevent->event, "filter", filter);
433 	if (retval < 0) {
434 		err_msg("Error enabling %s:%s filter %s\n", tevent->system,
435 			tevent->event ? : "*", tevent->filter);
436 		return 1;
437 	}
438 
439 	tevent->filter_enabled = 1;
440 	return 0;
441 }
442 
443 /*
444  * trace_event_enable_trigger - enable an event trigger associated with an event
445  */
446 static int trace_event_enable_trigger(struct trace_instance *instance,
447 				      struct trace_events *tevent)
448 {
449 	char trigger[1024];
450 	int retval;
451 
452 	if (!tevent->trigger)
453 		return 0;
454 
455 	if (!tevent->event) {
456 		err_msg("Trigger %s applies only for single events, not for all %s:* events\n",
457 			tevent->trigger, tevent->system);
458 		return 1;
459 	}
460 
461 	snprintf(trigger, 1024, "%s\n", tevent->trigger);
462 
463 	debug_msg("Enabling %s:%s trigger %s\n", tevent->system,
464 		  tevent->event ? : "*", tevent->trigger);
465 
466 	retval = tracefs_event_file_write(instance->inst, tevent->system,
467 					  tevent->event, "trigger", trigger);
468 	if (retval < 0) {
469 		err_msg("Error enabling %s:%s trigger %s\n", tevent->system,
470 			tevent->event ? : "*", tevent->trigger);
471 		return 1;
472 	}
473 
474 	tevent->trigger_enabled = 1;
475 
476 	return 0;
477 }
478 
479 /*
480  * trace_events_enable - enable all events
481  */
482 int trace_events_enable(struct trace_instance *instance,
483 			struct trace_events *events)
484 {
485 	struct trace_events *tevent = events;
486 	int retval;
487 
488 	while (tevent) {
489 		debug_msg("Enabling event %s:%s\n", tevent->system, tevent->event ? : "*");
490 		retval = tracefs_event_enable(instance->inst, tevent->system, tevent->event);
491 		if (retval < 0) {
492 			err_msg("Error enabling event %s:%s\n", tevent->system,
493 				tevent->event ? : "*");
494 			return 1;
495 		}
496 
497 		retval = trace_event_enable_filter(instance, tevent);
498 		if (retval)
499 			return 1;
500 
501 		retval = trace_event_enable_trigger(instance, tevent);
502 		if (retval)
503 			return 1;
504 
505 		tevent->enabled = 1;
506 		tevent = tevent->next;
507 	}
508 
509 	return 0;
510 }
511 
512 /*
513  * trace_events_destroy - disable and free all trace events
514  */
515 void trace_events_destroy(struct trace_instance *instance,
516 			  struct trace_events *events)
517 {
518 	if (!events)
519 		return;
520 
521 	trace_events_disable(instance, events);
522 	trace_events_free(events);
523 }
524 
525 int trace_is_off(struct trace_instance *tool, struct trace_instance *trace)
526 {
527 	/*
528 	 * The tool instance is always present, it is the one used to collect
529 	 * data.
530 	 */
531 	if (!tracefs_trace_is_on(tool->inst))
532 		return 1;
533 
534 	/*
535 	 * The trace instance is only enabled when -t is set. IOW, when the system
536 	 * is tracing.
537 	 */
538 	if (trace && !tracefs_trace_is_on(trace->inst))
539 		return 1;
540 
541 	return 0;
542 }
543