xref: /openbmc/linux/tools/tracing/rtla/src/trace.c (revision 1fd02f66)
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 	}
138 
139 	if (trace->seq)
140 		free(trace->seq);
141 
142 	if (trace->tep)
143 		tep_free(trace->tep);
144 }
145 
146 /*
147  * trace_instance_init - create an rtla trace instance
148  *
149  * It is more than the tracefs instance, as it contains other
150  * things required for the tracing, such as the local events and
151  * a seq file.
152  *
153  * Note that the trace instance is returned disabled. This allows
154  * the tool to apply some other configs, like setting priority
155  * to the kernel threads, before starting generating trace entries.
156  */
157 int trace_instance_init(struct trace_instance *trace, char *tool_name)
158 {
159 	trace->seq = calloc(1, sizeof(*trace->seq));
160 	if (!trace->seq)
161 		goto out_err;
162 
163 	trace_seq_init(trace->seq);
164 
165 	trace->inst = create_instance(tool_name);
166 	if (!trace->inst)
167 		goto out_err;
168 
169 	trace->tep = tracefs_local_events(NULL);
170 	if (!trace->tep)
171 		goto out_err;
172 
173 	/*
174 	 * Let the main enable the record after setting some other
175 	 * things such as the priority of the tracer's threads.
176 	 */
177 	tracefs_trace_off(trace->inst);
178 
179 	return 0;
180 
181 out_err:
182 	trace_instance_destroy(trace);
183 	return 1;
184 }
185 
186 /*
187  * trace_instance_start - start tracing a given rtla instance
188  */
189 int trace_instance_start(struct trace_instance *trace)
190 {
191 	return tracefs_trace_on(trace->inst);
192 }
193 
194 /*
195  * trace_events_free - free a list of trace events
196  */
197 static void trace_events_free(struct trace_events *events)
198 {
199 	struct trace_events *tevent = events;
200 	struct trace_events *free_event;
201 
202 	while (tevent) {
203 		free_event = tevent;
204 
205 		tevent = tevent->next;
206 
207 		if (free_event->filter)
208 			free(free_event->filter);
209 		if (free_event->trigger)
210 			free(free_event->trigger);
211 		free(free_event->system);
212 		free(free_event);
213 	}
214 }
215 
216 /*
217  * trace_event_alloc - alloc and parse a single trace event
218  */
219 struct trace_events *trace_event_alloc(const char *event_string)
220 {
221 	struct trace_events *tevent;
222 
223 	tevent = calloc(1, sizeof(*tevent));
224 	if (!tevent)
225 		return NULL;
226 
227 	tevent->system = strdup(event_string);
228 	if (!tevent->system) {
229 		free(tevent);
230 		return NULL;
231 	}
232 
233 	tevent->event = strstr(tevent->system, ":");
234 	if (tevent->event) {
235 		*tevent->event = '\0';
236 		tevent->event = &tevent->event[1];
237 	}
238 
239 	return tevent;
240 }
241 
242 /*
243  * trace_event_add_filter - record an event filter
244  */
245 int trace_event_add_filter(struct trace_events *event, char *filter)
246 {
247 	if (event->filter)
248 		free(event->filter);
249 
250 	event->filter = strdup(filter);
251 	if (!event->filter)
252 		return 1;
253 
254 	return 0;
255 }
256 
257 /*
258  * trace_event_add_trigger - record an event trigger action
259  */
260 int trace_event_add_trigger(struct trace_events *event, char *trigger)
261 {
262 	if (event->trigger)
263 		free(event->trigger);
264 
265 	event->trigger = strdup(trigger);
266 	if (!event->trigger)
267 		return 1;
268 
269 	return 0;
270 }
271 
272 /*
273  * trace_event_disable_filter - disable an event filter
274  */
275 static void trace_event_disable_filter(struct trace_instance *instance,
276 				       struct trace_events *tevent)
277 {
278 	char filter[1024];
279 	int retval;
280 
281 	if (!tevent->filter)
282 		return;
283 
284 	if (!tevent->filter_enabled)
285 		return;
286 
287 	debug_msg("Disabling %s:%s filter %s\n", tevent->system,
288 		  tevent->event ? : "*", tevent->filter);
289 
290 	snprintf(filter, 1024, "!%s\n", tevent->filter);
291 
292 	retval = tracefs_event_file_write(instance->inst, tevent->system,
293 					  tevent->event, "filter", filter);
294 	if (retval < 0)
295 		err_msg("Error disabling %s:%s filter %s\n", tevent->system,
296 			tevent->event ? : "*", tevent->filter);
297 }
298 
299 /*
300  * trace_event_save_hist - save the content of an event hist
301  *
302  * If the trigger is a hist: one, save the content of the hist file.
303  */
304 static void trace_event_save_hist(struct trace_instance *instance,
305 				  struct trace_events *tevent)
306 {
307 	int retval, index, out_fd;
308 	mode_t mode = 0644;
309 	char path[1024];
310 	char *hist;
311 
312 	if (!tevent)
313 		return;
314 
315 	/* trigger enables hist */
316 	if (!tevent->trigger)
317 		return;
318 
319 	/* is this a hist: trigger? */
320 	retval = strncmp(tevent->trigger, "hist:", strlen("hist:"));
321 	if (retval)
322 		return;
323 
324 	snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event);
325 
326 	printf("  Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path);
327 
328 	out_fd = creat(path, mode);
329 	if (out_fd < 0) {
330 		err_msg("  Failed to create %s output file\n", path);
331 		return;
332 	}
333 
334 	hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0);
335 	if (!hist) {
336 		err_msg("  Failed to read %s:%s hist file\n", tevent->system, tevent->event);
337 		goto out_close;
338 	}
339 
340 	index = 0;
341 	do {
342 		index += write(out_fd, &hist[index], strlen(hist) - index);
343 	} while (index < strlen(hist));
344 
345 	free(hist);
346 out_close:
347 	close(out_fd);
348 }
349 
350 /*
351  * trace_event_disable_trigger - disable an event trigger
352  */
353 static void trace_event_disable_trigger(struct trace_instance *instance,
354 					struct trace_events *tevent)
355 {
356 	char trigger[1024];
357 	int retval;
358 
359 	if (!tevent->trigger)
360 		return;
361 
362 	if (!tevent->trigger_enabled)
363 		return;
364 
365 	debug_msg("Disabling %s:%s trigger %s\n", tevent->system,
366 		  tevent->event ? : "*", tevent->trigger);
367 
368 	trace_event_save_hist(instance, tevent);
369 
370 	snprintf(trigger, 1024, "!%s\n", tevent->trigger);
371 
372 	retval = tracefs_event_file_write(instance->inst, tevent->system,
373 					  tevent->event, "trigger", trigger);
374 	if (retval < 0)
375 		err_msg("Error disabling %s:%s trigger %s\n", tevent->system,
376 			tevent->event ? : "*", tevent->trigger);
377 }
378 
379 /*
380  * trace_events_disable - disable all trace events
381  */
382 void trace_events_disable(struct trace_instance *instance,
383 			  struct trace_events *events)
384 {
385 	struct trace_events *tevent = events;
386 
387 	if (!events)
388 		return;
389 
390 	while (tevent) {
391 		debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*");
392 		if (tevent->enabled) {
393 			trace_event_disable_filter(instance, tevent);
394 			trace_event_disable_trigger(instance, tevent);
395 			tracefs_event_disable(instance->inst, tevent->system, tevent->event);
396 		}
397 
398 		tevent->enabled = 0;
399 		tevent = tevent->next;
400 	}
401 }
402 
403 /*
404  * trace_event_enable_filter - enable an event filter associated with an event
405  */
406 static int trace_event_enable_filter(struct trace_instance *instance,
407 				     struct trace_events *tevent)
408 {
409 	char filter[1024];
410 	int retval;
411 
412 	if (!tevent->filter)
413 		return 0;
414 
415 	if (!tevent->event) {
416 		err_msg("Filter %s applies only for single events, not for all %s:* events\n",
417 			tevent->filter, tevent->system);
418 		return 1;
419 	}
420 
421 	snprintf(filter, 1024, "%s\n", tevent->filter);
422 
423 	debug_msg("Enabling %s:%s filter %s\n", tevent->system,
424 		  tevent->event ? : "*", tevent->filter);
425 
426 	retval = tracefs_event_file_write(instance->inst, tevent->system,
427 					  tevent->event, "filter", filter);
428 	if (retval < 0) {
429 		err_msg("Error enabling %s:%s filter %s\n", tevent->system,
430 			tevent->event ? : "*", tevent->filter);
431 		return 1;
432 	}
433 
434 	tevent->filter_enabled = 1;
435 	return 0;
436 }
437 
438 /*
439  * trace_event_enable_trigger - enable an event trigger associated with an event
440  */
441 static int trace_event_enable_trigger(struct trace_instance *instance,
442 				      struct trace_events *tevent)
443 {
444 	char trigger[1024];
445 	int retval;
446 
447 	if (!tevent->trigger)
448 		return 0;
449 
450 	if (!tevent->event) {
451 		err_msg("Trigger %s applies only for single events, not for all %s:* events\n",
452 			tevent->trigger, tevent->system);
453 		return 1;
454 	}
455 
456 	snprintf(trigger, 1024, "%s\n", tevent->trigger);
457 
458 	debug_msg("Enabling %s:%s trigger %s\n", tevent->system,
459 		  tevent->event ? : "*", tevent->trigger);
460 
461 	retval = tracefs_event_file_write(instance->inst, tevent->system,
462 					  tevent->event, "trigger", trigger);
463 	if (retval < 0) {
464 		err_msg("Error enabling %s:%s trigger %s\n", tevent->system,
465 			tevent->event ? : "*", tevent->trigger);
466 		return 1;
467 	}
468 
469 	tevent->trigger_enabled = 1;
470 
471 	return 0;
472 }
473 
474 /*
475  * trace_events_enable - enable all events
476  */
477 int trace_events_enable(struct trace_instance *instance,
478 			struct trace_events *events)
479 {
480 	struct trace_events *tevent = events;
481 	int retval;
482 
483 	while (tevent) {
484 		debug_msg("Enabling event %s:%s\n", tevent->system, tevent->event ? : "*");
485 		retval = tracefs_event_enable(instance->inst, tevent->system, tevent->event);
486 		if (retval < 0) {
487 			err_msg("Error enabling event %s:%s\n", tevent->system,
488 				tevent->event ? : "*");
489 			return 1;
490 		}
491 
492 		retval = trace_event_enable_filter(instance, tevent);
493 		if (retval)
494 			return 1;
495 
496 		retval = trace_event_enable_trigger(instance, tevent);
497 		if (retval)
498 			return 1;
499 
500 		tevent->enabled = 1;
501 		tevent = tevent->next;
502 	}
503 
504 	return 0;
505 }
506 
507 /*
508  * trace_events_destroy - disable and free all trace events
509  */
510 void trace_events_destroy(struct trace_instance *instance,
511 			  struct trace_events *events)
512 {
513 	if (!events)
514 		return;
515 
516 	trace_events_disable(instance, events);
517 	trace_events_free(events);
518 }
519 
520 int trace_is_off(struct trace_instance *tool, struct trace_instance *trace)
521 {
522 	/*
523 	 * The tool instance is always present, it is the one used to collect
524 	 * data.
525 	 */
526 	if (!tracefs_trace_is_on(tool->inst))
527 		return 1;
528 
529 	/*
530 	 * The trace instance is only enabled when -t is set. IOW, when the system
531 	 * is tracing.
532 	 */
533 	if (trace && !tracefs_trace_is_on(trace->inst))
534 		return 1;
535 
536 	return 0;
537 }
538