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