projects/linux·blob master · refs

597 lines · 13.2 KiB

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