• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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