1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
4 *
5 * Updates:
6 * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
7 *
8 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <dirent.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <limits.h>
16
17 #include "tracefs.h"
18 #include "tracefs-local.h"
19
20 #define DYNEVENTS_EVENTS "dynamic_events"
21 #define KPROBE_EVENTS "kprobe_events"
22 #define UPROBE_EVENTS "uprobe_events"
23 #define SYNTH_EVENTS "synthetic_events"
24 #define DYNEVENTS_DEFAULT_GROUP "dynamic"
25
26 #define EVENT_INDEX(B) (ffs(B) - 1)
27
28 struct dyn_events_desc;
29 static int dyn_generic_parse(struct dyn_events_desc *,
30 const char *, char *, struct tracefs_dynevent **);
31 static int dyn_synth_parse(struct dyn_events_desc *,
32 const char *, char *, struct tracefs_dynevent **);
33 static int dyn_generic_del(struct dyn_events_desc *, struct tracefs_dynevent *);
34 static int dyn_synth_del(struct dyn_events_desc *, struct tracefs_dynevent *);
35
36 struct dyn_events_desc {
37 enum tracefs_dynevent_type type;
38 const char *file;
39 const char *prefix;
40 int (*del)(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn);
41 int (*parse)(struct dyn_events_desc *desc, const char *group,
42 char *line, struct tracefs_dynevent **ret_dyn);
43 } dynevents[] = {
44 {TRACEFS_DYNEVENT_KPROBE, KPROBE_EVENTS, "p", dyn_generic_del, dyn_generic_parse},
45 {TRACEFS_DYNEVENT_KRETPROBE, KPROBE_EVENTS, "r", dyn_generic_del, dyn_generic_parse},
46 {TRACEFS_DYNEVENT_UPROBE, UPROBE_EVENTS, "p", dyn_generic_del, dyn_generic_parse},
47 {TRACEFS_DYNEVENT_URETPROBE, UPROBE_EVENTS, "r", dyn_generic_del, dyn_generic_parse},
48 {TRACEFS_DYNEVENT_EPROBE, "", "e", dyn_generic_del, dyn_generic_parse},
49 {TRACEFS_DYNEVENT_SYNTH, SYNTH_EVENTS, "", dyn_synth_del, dyn_synth_parse},
50 };
51
52
53
dyn_generic_del(struct dyn_events_desc * desc,struct tracefs_dynevent * dyn)54 static int dyn_generic_del(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn)
55 {
56 char *str;
57 int ret;
58
59 if (dyn->system)
60 ret = asprintf(&str, "-:%s/%s", dyn->system, dyn->event);
61 else
62 ret = asprintf(&str, "-:%s", dyn->event);
63
64 if (ret < 0)
65 return -1;
66
67 ret = tracefs_instance_file_append(NULL, desc->file, str);
68 free(str);
69
70 return ret < 0 ? ret : 0;
71 }
72
73 /**
74 * tracefs_dynevent_free - Free a dynamic event context
75 * @devent: Pointer to a dynamic event context
76 *
77 * The dynamic event, described by this context, is not
78 * removed from the system by this API. It only frees the memory.
79 */
tracefs_dynevent_free(struct tracefs_dynevent * devent)80 void tracefs_dynevent_free(struct tracefs_dynevent *devent)
81 {
82 if (!devent)
83 return;
84 free(devent->system);
85 free(devent->event);
86 free(devent->address);
87 free(devent->format);
88 free(devent->prefix);
89 free(devent->trace_file);
90 free(devent);
91 }
92
parse_prefix(char * word,char ** prefix,char ** system,char ** name)93 static void parse_prefix(char *word, char **prefix, char **system, char **name)
94 {
95 char *sav;
96
97 *prefix = NULL;
98 *system = NULL;
99 *name = NULL;
100
101 *prefix = strtok_r(word, ":", &sav);
102 *system = strtok_r(NULL, "/", &sav);
103 if (!(*system))
104 return;
105
106 *name = strtok_r(NULL, " \t", &sav);
107 if (!(*name)) {
108 *name = *system;
109 *system = NULL;
110 }
111 }
112
113 /*
114 * Parse lines from dynamic_events, kprobe_events and uprobe_events files
115 * PREFIX[:[SYSTEM/]EVENT] [ADDRSS] [FORMAT]
116 */
dyn_generic_parse(struct dyn_events_desc * desc,const char * group,char * line,struct tracefs_dynevent ** ret_dyn)117 static int dyn_generic_parse(struct dyn_events_desc *desc, const char *group,
118 char *line, struct tracefs_dynevent **ret_dyn)
119 {
120 struct tracefs_dynevent *dyn;
121 char *word;
122 char *format = NULL;
123 char *address = NULL;
124 char *system;
125 char *prefix;
126 char *event;
127 char *sav;
128
129 if (strncmp(line, desc->prefix, strlen(desc->prefix)))
130 return -1;
131
132 word = strtok_r(line, " \t", &sav);
133 if (!word || *word == '\0')
134 return -1;
135
136 parse_prefix(word, &prefix, &system, &event);
137 if (!prefix)
138 return -1;
139
140 if (desc->type != TRACEFS_DYNEVENT_SYNTH) {
141 address = strtok_r(NULL, " \t", &sav);
142 if (!address || *address == '\0')
143 return -1;
144 }
145
146 format = strtok_r(NULL, "", &sav);
147
148 /* KPROBEs and UPROBEs share the same prefix, check the format */
149 if (desc->type & (TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE)) {
150 if (!strchr(address, '/'))
151 return -1;
152 }
153
154 if (group && (!system || strcmp(group, system) != 0))
155 return -1;
156
157 if (!ret_dyn)
158 return 0;
159
160 dyn = calloc(1, sizeof(*dyn));
161 if (!dyn)
162 return -1;
163
164 dyn->type = desc->type;
165 dyn->trace_file = strdup(desc->file);
166 if (!dyn->trace_file)
167 goto error;
168
169 dyn->prefix = strdup(prefix);
170 if (!dyn->prefix)
171 goto error;
172
173 if (system) {
174 dyn->system = strdup(system);
175 if (!dyn->system)
176 goto error;
177 }
178
179 if (event) {
180 dyn->event = strdup(event);
181 if (!dyn->event)
182 goto error;
183 }
184
185 if (address) {
186 dyn->address = strdup(address);
187 if (!dyn->address)
188 goto error;
189 }
190
191 if (format) {
192 dyn->format = strdup(format);
193 if (!dyn->format)
194 goto error;
195 }
196
197 *ret_dyn = dyn;
198 return 0;
199 error:
200 tracefs_dynevent_free(dyn);
201 return -1;
202 }
203
dyn_synth_del(struct dyn_events_desc * desc,struct tracefs_dynevent * dyn)204 static int dyn_synth_del(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn)
205 {
206 char *str;
207 int ret;
208
209 if (!strcmp(desc->file, DYNEVENTS_EVENTS))
210 return dyn_generic_del(desc, dyn);
211
212 ret = asprintf(&str, "!%s", dyn->event);
213 if (ret < 0)
214 return -1;
215
216 ret = tracefs_instance_file_append(NULL, desc->file, str);
217 free(str);
218
219 return ret < 0 ? ret : 0;
220 }
221
222 /*
223 * Parse lines from synthetic_events file
224 * EVENT ARG [ARG]
225 */
dyn_synth_parse(struct dyn_events_desc * desc,const char * group,char * line,struct tracefs_dynevent ** ret_dyn)226 static int dyn_synth_parse(struct dyn_events_desc *desc, const char *group,
227 char *line, struct tracefs_dynevent **ret_dyn)
228 {
229 struct tracefs_dynevent *dyn;
230 char *format;
231 char *event;
232 char *sav;
233
234 if (!strcmp(desc->file, DYNEVENTS_EVENTS))
235 return dyn_generic_parse(desc, group, line, ret_dyn);
236
237 /* synthetic_events file has slightly different syntax */
238 event = strtok_r(line, " \t", &sav);
239 if (!event || *event == '\0')
240 return -1;
241
242 format = strtok_r(NULL, "", &sav);
243 if (!format || *format == '\0')
244 return -1;
245
246 if (!ret_dyn)
247 return 0;
248
249 dyn = calloc(1, sizeof(*dyn));
250 if (!dyn)
251 return -1;
252
253 dyn->type = desc->type;
254 dyn->trace_file = strdup(desc->file);
255 if (!dyn->trace_file)
256 goto error;
257
258 dyn->event = strdup(event);
259 if (!dyn->event)
260 goto error;
261
262 dyn->format = strdup(format+1);
263 if (!dyn->format)
264 goto error;
265
266 *ret_dyn = dyn;
267 return 0;
268 error:
269 tracefs_dynevent_free(dyn);
270 return -1;
271 }
272
init_devent_desc(void)273 static void init_devent_desc(void)
274 {
275 int i;
276
277 BUILD_BUG_ON(ARRAY_SIZE(dynevents) != EVENT_INDEX(TRACEFS_DYNEVENT_MAX));
278
279 if (!tracefs_file_exists(NULL, DYNEVENTS_EVENTS))
280 return;
281
282 /* Use ftrace dynamic_events, if available */
283 for (i = 0; i < EVENT_INDEX(TRACEFS_DYNEVENT_MAX); i++)
284 dynevents[i].file = DYNEVENTS_EVENTS;
285
286 dynevents[EVENT_INDEX(TRACEFS_DYNEVENT_SYNTH)].prefix = "s";
287 }
288
get_devent_desc(enum tracefs_dynevent_type type)289 static struct dyn_events_desc *get_devent_desc(enum tracefs_dynevent_type type)
290 {
291
292 static bool init;
293
294 if (type >= TRACEFS_DYNEVENT_MAX)
295 return NULL;
296
297 if (!init) {
298 init_devent_desc();
299 init = true;
300 }
301
302 return &dynevents[EVENT_INDEX(type)];
303 }
304
305 /**
306 * dynevent_alloc - Allocate new dynamic event
307 * @type: Type of the dynamic event
308 * @system: The system name (NULL for the default dynamic)
309 * @event: Name of the event
310 * @addr: The function and offset (or address) to insert the probe
311 * @format: The format string to define the probe.
312 *
313 * Allocate a dynamic event context that will be in the @system group
314 * (or dynamic if @system is NULL). Have the name of @event and
315 * will be associated to @addr, if applicable for that event type
316 * (function name, with or without offset, or a address). And the @format will
317 * define the format of the kprobe.
318 * The dynamic event is not created in the system.
319 *
320 * Return a pointer to a dynamic event context on success, or NULL on error.
321 * The returned pointer must be freed with tracefs_dynevent_free()
322 *
323 * errno will be set to EINVAL if event is NULL.
324 */
325 __hidden struct tracefs_dynevent *
dynevent_alloc(enum tracefs_dynevent_type type,const char * system,const char * event,const char * address,const char * format)326 dynevent_alloc(enum tracefs_dynevent_type type, const char *system,
327 const char *event, const char *address, const char *format)
328 {
329 struct tracefs_dynevent *devent;
330 struct dyn_events_desc *desc;
331
332 if (!event) {
333 errno = EINVAL;
334 return NULL;
335 }
336
337 desc = get_devent_desc(type);
338 if (!desc || !desc->file) {
339 errno = ENOTSUP;
340 return NULL;
341 }
342
343 devent = calloc(1, sizeof(*devent));
344 if (!devent)
345 return NULL;
346
347 devent->type = type;
348 devent->trace_file = strdup(desc->file);
349 if (!devent->trace_file)
350 goto err;
351
352 if (!system)
353 system = DYNEVENTS_DEFAULT_GROUP;
354 devent->system = strdup(system);
355 if (!devent->system)
356 goto err;
357
358 devent->event = strdup(event);
359 if (!devent->event)
360 goto err;
361
362 devent->prefix = strdup(desc->prefix);
363 if (!devent->prefix)
364 goto err;
365
366 if (address) {
367 devent->address = strdup(address);
368 if (!devent->address)
369 goto err;
370 }
371 if (format) {
372 devent->format = strdup(format);
373 if (!devent->format)
374 goto err;
375 }
376
377 return devent;
378 err:
379 tracefs_dynevent_free(devent);
380 return NULL;
381 }
382
383 /**
384 * tracefs_dynevent_create - Create a dynamic event in the system
385 * @devent: Pointer to a dynamic event context, describing the event
386 *
387 * Return 0 on success, or -1 on error.
388 */
tracefs_dynevent_create(struct tracefs_dynevent * devent)389 int tracefs_dynevent_create(struct tracefs_dynevent *devent)
390 {
391 char *str;
392 int ret;
393
394 if (!devent)
395 return -1;
396
397 if (devent->system && devent->system[0])
398 ret = asprintf(&str, "%s%s%s/%s %s %s\n",
399 devent->prefix, strlen(devent->prefix) ? ":" : "",
400 devent->system, devent->event,
401 devent->address ? devent->address : "",
402 devent->format ? devent->format : "");
403 else
404 ret = asprintf(&str, "%s%s%s %s %s\n",
405 devent->prefix, strlen(devent->prefix) ? ":" : "",
406 devent->event,
407 devent->address ? devent->address : "",
408 devent->format ? devent->format : "");
409 if (ret < 0)
410 return -1;
411
412 ret = tracefs_instance_file_append(NULL, devent->trace_file, str);
413 free(str);
414
415 return ret < 0 ? ret : 0;
416 }
417
disable_events(const char * system,const char * event,char ** list)418 static void disable_events(const char *system, const char *event,
419 char **list)
420 {
421 struct tracefs_instance *instance;
422 int i;
423
424 /*
425 * Note, this will not fail even on error.
426 * That is because even if something fails, it may still
427 * work enough to clear the kprobes. If that's the case
428 * the clearing after the loop will succeed and the function
429 * is a success, even though other parts had failed. If
430 * one of the kprobe events is enabled in one of the
431 * instances that fail, then the clearing will fail too
432 * and the function will return an error.
433 */
434
435 tracefs_event_disable(NULL, system, event);
436 /* No need to test results */
437
438 if (!list)
439 return;
440
441 for (i = 0; list[i]; i++) {
442 instance = tracefs_instance_alloc(NULL, list[i]);
443 /* If this fails, try the next one */
444 if (!instance)
445 continue;
446 tracefs_event_disable(instance, system, event);
447 tracefs_instance_free(instance);
448 }
449 }
450
451 /**
452 * tracefs_dynevent_destroy - Remove a dynamic event from the system
453 * @devent: A dynamic event context, describing the dynamic event that will be deleted.
454 * @force: Will attempt to disable all events before removing them.
455 *
456 * The dynamic event context is not freed by this API. It only removes the event from the system.
457 * If there are any enabled events, and @force is not set, then it will error with -1 and errno
458 * to be EBUSY.
459 *
460 * Return 0 on success, or -1 on error.
461 */
tracefs_dynevent_destroy(struct tracefs_dynevent * devent,bool force)462 int tracefs_dynevent_destroy(struct tracefs_dynevent *devent, bool force)
463 {
464 struct dyn_events_desc *desc;
465 char **instance_list;
466
467 if (!devent)
468 return -1;
469
470 if (force) {
471 instance_list = tracefs_instances(NULL);
472 disable_events(devent->system, devent->event, instance_list);
473 tracefs_list_free(instance_list);
474 }
475
476 desc = get_devent_desc(devent->type);
477 if (!desc)
478 return -1;
479
480 return desc->del(desc, devent);
481 }
482
get_all_dynevents(enum tracefs_dynevent_type type,const char * system,struct tracefs_dynevent *** ret_all)483 static int get_all_dynevents(enum tracefs_dynevent_type type, const char *system,
484 struct tracefs_dynevent ***ret_all)
485 {
486 struct dyn_events_desc *desc;
487 struct tracefs_dynevent *devent, **tmp, **all = NULL;
488 char *content;
489 int count = 0;
490 char *line;
491 char *next;
492 int ret;
493
494 desc = get_devent_desc(type);
495 if (!desc)
496 return -1;
497
498 content = tracefs_instance_file_read(NULL, desc->file, NULL);
499 if (!content)
500 return -1;
501
502 line = content;
503 do {
504 next = strchr(line, '\n');
505 if (next)
506 *next = '\0';
507 ret = desc->parse(desc, system, line, ret_all ? &devent : NULL);
508 if (!ret) {
509 if (ret_all) {
510 tmp = realloc(all, (count + 1) * sizeof(*tmp));
511 if (!tmp)
512 goto error;
513 all = tmp;
514 all[count] = devent;
515 }
516 count++;
517 }
518 line = next + 1;
519 } while (next);
520
521 free(content);
522 if (ret_all)
523 *ret_all = all;
524 return count;
525
526 error:
527 free(content);
528 free(all);
529 return -1;
530 }
531
532 /**
533 * tracefs_dynevent_list_free - Deletes an array of pointers to dynamic event contexts
534 * @events: An array of pointers to dynamic event contexts. The last element of the array
535 * must be a NULL pointer.
536 */
tracefs_dynevent_list_free(struct tracefs_dynevent ** events)537 void tracefs_dynevent_list_free(struct tracefs_dynevent **events)
538 {
539 int i;
540
541 if (!events)
542 return;
543
544 for (i = 0; events[i]; i++)
545 tracefs_dynevent_free(events[i]);
546
547 free(events);
548 }
549
550 /**
551 * tracefs_dynevent_get_all - return an array of pointers to dynamic events of given types
552 * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
553 * are considered.
554 * @system: Get events from that system only. If @system is NULL, events from all systems
555 * are returned.
556 *
557 * Returns an array of pointers to dynamic events of given types that exist in the system.
558 * The array must be freed with tracefs_dynevent_list_free(). If there are no events a NULL
559 * pointer is returned.
560 */
561 struct tracefs_dynevent **
tracefs_dynevent_get_all(unsigned int types,const char * system)562 tracefs_dynevent_get_all(unsigned int types, const char *system)
563 {
564 struct tracefs_dynevent **events, **tmp, **all_events = NULL;
565 int count, all = 0;
566 int i;
567
568 for (i = 1; i < TRACEFS_DYNEVENT_MAX; i <<= 1) {
569 if (types) {
570 if (i > types)
571 break;
572 if (!(types & i))
573 continue;
574 }
575 count = get_all_dynevents(i, system, &events);
576 if (count > 0) {
577 tmp = realloc(all_events, (all + count + 1) * sizeof(*tmp));
578 if (!tmp)
579 goto error;
580 all_events = tmp;
581 memcpy(all_events + all, events, count * sizeof(*events));
582 all += count;
583 /* Add a NULL pointer at the end */
584 all_events[all] = NULL;
585 free(events);
586 }
587 }
588
589 return all_events;
590
591 error:
592 if (all_events) {
593 for (i = 0; i < all; i++)
594 free(all_events[i]);
595 free(all_events);
596 }
597 return NULL;
598 }
599
600 /**
601 * tracefs_dynevent_get - return a single dynamic event if it exists
602 * @type; Dynamic event type
603 * @system: Get events from that system only. May be NULL.
604 * @event: Get event of the system type (may not be NULL)
605 *
606 * Returns the dynamic event of the given @type and @system for with the @event
607 * name. If @system is NULL, it will return the first dynamic event that it finds
608 * that matches the @event name.
609 *
610 * The returned event must be freed with tracefs_dynevent_free().
611 * NULL is returned if no event match is found, or other error.
612 */
613 struct tracefs_dynevent *
tracefs_dynevent_get(enum tracefs_dynevent_type type,const char * system,const char * event)614 tracefs_dynevent_get(enum tracefs_dynevent_type type, const char *system,
615 const char *event)
616 {
617 struct tracefs_dynevent **events;
618 struct tracefs_dynevent *devent = NULL;
619 int count;
620 int i;
621
622 if (!event) {
623 errno = -EINVAL;
624 return NULL;
625 }
626
627 count = get_all_dynevents(type, system, &events);
628 if (count <= 0)
629 return NULL;
630
631 for (i = 0; i < count; i++) {
632 if (strcmp(events[i]->event, event) == 0)
633 break;
634 }
635 if (i < count) {
636 devent = events[i];
637 events[i] = NULL;
638 }
639
640 tracefs_dynevent_list_free(events);
641
642 return devent;
643 }
644
645 /**
646 * tracefs_dynevent_destroy_all - removes all dynamic events of given types from the system
647 * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
648 * are considered.
649 * @force: Will attempt to disable all events before removing them.
650 *
651 * Will remove all dynamic events of the given types from the system. If there are any enabled
652 * events, and @force is not set, then the removal of these will fail. If @force is set, then
653 * it will attempt to disable all the events in all instances before removing them.
654 *
655 * Returns zero if all requested events are removed successfully, or -1 if some of them are not
656 * removed.
657 */
tracefs_dynevent_destroy_all(unsigned int types,bool force)658 int tracefs_dynevent_destroy_all(unsigned int types, bool force)
659 {
660 struct tracefs_dynevent **all;
661 int ret = 0;
662 int i;
663
664 all = tracefs_dynevent_get_all(types, NULL);
665 if (!all)
666 return 0;
667
668 for (i = 0; all[i]; i++) {
669 if (tracefs_dynevent_destroy(all[i], force))
670 ret = -1;
671 }
672
673 tracefs_dynevent_list_free(all);
674
675 return ret;
676 }
677
678 /**
679 * dynevent_get_count - Count dynamic events of given types and system
680 * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
681 * are considered.
682 * @system: Count events from that system only. If @system is NULL, events from all systems
683 * are counted.
684 *
685 * Return the count of requested dynamic events
686 */
dynevent_get_count(unsigned int types,const char * system)687 __hidden int dynevent_get_count(unsigned int types, const char *system)
688 {
689 int count, all = 0;
690 int i;
691
692 for (i = 1; i < TRACEFS_DYNEVENT_MAX; i <<= 1) {
693 if (types) {
694 if (i > types)
695 break;
696 if (!(types & i))
697 continue;
698 }
699 count = get_all_dynevents(i, system, NULL);
700 if (count > 0)
701 all += count;
702 }
703
704 return all;
705 }
706
707 static enum tracefs_dynevent_type
dynevent_info(struct tracefs_dynevent * dynevent,char ** system,char ** event,char ** prefix,char ** addr,char ** format)708 dynevent_info(struct tracefs_dynevent *dynevent, char **system,
709 char **event, char **prefix, char **addr, char **format)
710 {
711 char **lv[] = { system, event, prefix, addr, format };
712 char **rv[] = { &dynevent->system, &dynevent->event, &dynevent->prefix,
713 &dynevent->address, &dynevent->format };
714 int i;
715
716 for (i = 0; i < ARRAY_SIZE(lv); i++) {
717 if (lv[i]) {
718 if (*rv[i]) {
719 *lv[i] = strdup(*rv[i]);
720 if (!*lv[i])
721 goto error;
722 } else {
723 *lv[i] = NULL;
724 }
725 }
726 }
727
728 return dynevent->type;
729
730 error:
731 for (i--; i >= 0; i--) {
732 if (lv[i])
733 free(*lv[i]);
734 }
735
736 return TRACEFS_DYNEVENT_UNKNOWN;
737 }
738
739 /**
740 * tracefs_dynevent_info - return details of a dynamic event
741 * @dynevent: A dynamic event context, describing given dynamic event.
742 * @group: return, group in which the dynamic event is configured
743 * @event: return, name of the dynamic event
744 * @prefix: return, prefix string of the dynamic event
745 * @addr: return, the function and offset (or address) of the dynamic event
746 * @format: return, the format string of the dynamic event
747 *
748 * Returns the type of the dynamic event, or TRACEFS_DYNEVENT_UNKNOWN in case of an error.
749 * Any of the @group, @event, @prefix, @addr and @format parameters are optional.
750 * If a valid pointer is passed, in case of success - a string is allocated and returned.
751 * These strings must be freed with free().
752 */
753 enum tracefs_dynevent_type
tracefs_dynevent_info(struct tracefs_dynevent * dynevent,char ** system,char ** event,char ** prefix,char ** addr,char ** format)754 tracefs_dynevent_info(struct tracefs_dynevent *dynevent, char **system,
755 char **event, char **prefix, char **addr, char **format)
756 {
757 if (!dynevent)
758 return TRACEFS_DYNEVENT_UNKNOWN;
759
760 return dynevent_info(dynevent, system, event, prefix, addr, format);
761 }
762
763 /**
764 * tracefs_dynevent_get_event - return tep event representing the given dynamic event
765 * @tep: a handle to the trace event parser context that holds the events
766 * @dynevent: a dynamic event context, describing given dynamic event.
767 *
768 * Returns a pointer to a tep event describing the given dynamic event. The pointer
769 * is managed by the @tep handle and must not be freed. In case of an error, or in case
770 * the requested dynamic event is missing in the @tep handler - NULL is returned.
771 */
772 struct tep_event *
tracefs_dynevent_get_event(struct tep_handle * tep,struct tracefs_dynevent * dynevent)773 tracefs_dynevent_get_event(struct tep_handle *tep, struct tracefs_dynevent *dynevent)
774 {
775 if (!tep || !dynevent || !dynevent->event)
776 return NULL;
777
778 return get_tep_event(tep, dynevent->system, dynevent->event);
779 }
780