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