LIBTRACEFS(3)
β¨ LIBTRACEFS(3) β the libtracefs girlies' era π
okay so this is libtracefs(3), section 3 of the manual, and it's honestly a whole vibe. bestie, we're talking dynamic events β creating them, destroying them, freeing them, and getting them back out of the kernel. let's go π¦
πΈ NAME πΈ
tracefs_dynevent_create, tracefs_dynevent_destroy, tracefs_dynevent_destroy_all, tracefs_dynevent_free, tracefs_dynevent_list_free, tracefs_dynevent_get, tracefs_dynevent_get_all, tracefs_dynevent_info, tracefs_dynevent_get_event β Create, destroy, free and get dynamic events. periodt. π
π SYNOPSIS π
#include <tracefs.h>
struct tracefs_dynevent;
enum tracefs_dynevent_type;
int tracefs_dynevent_create(struct tracefs_dynevent *devent);
int tracefs_dynevent_destroy(struct tracefs_dynevent *devent, bool force);
int tracefs_dynevent_destroy_all(unsigned int types, bool force);
void tracefs_dynevent_free(struct tracefs_dynevent *devent);
void tracefs_dynevent_list_free(struct tracefs_dynevent **events);
struct tracefs_dynevent *tracefs_dynevent_get(enum tracefs_dynevent_type type, const char *system, const char *event);
struct tracefs_dynevent **tracefs_dynevent_get_all(unsigned int types, const char *system);
enum tracefs_dynevent_type tracefs_dynevent_info(struct tracefs_dynevent *dynevent, char **system, char **event, char **prefix, char **addr, char **format);
struct tep_event *tracefs_dynevent_get_event(struct tep_handle *tep, struct tracefs_dynevent *dynevent);
like, that's the whole lineup. iconic honestly. π
π¦ DESCRIPTION π¦
okay so picture this: dynamic events are little homemade tracepoints you cook up yourself, and this whole function family is your kitchen utensils. let me walk you through each one, bestie. π
tracefs_dynevent_create() β this one creates the dynamic event devent in the system. that's it. that's the whole job. slay. β¨
tracefs_dynevent_destroy() β removes the dynamic event devent from the system. if force is true, it will attempt to disable all events in all trace instances before removing the dynamic event. heads up though β the devent context is not freed here, so please use tracefs_dynevent_free() to free it. don't leave it dangling bestie π
tracefs_dynevent_destroy_all() β removes all dynamic events of the given types from the system. the types parameter is either a type of one specific dynamic event, or a bitmask of dynamic event types tracefs_dynevent_type, that will be removed. if types is 0, dynamic events from all types get removed. and if force is true, the function will attempt to disable all events in all trace instances before removing the dynamic events. ngl that's a lot of power π
tracefs_dynevent_get() β allocates and returns a single instance of a dynamic event that matches the given type, system and event you pass in. returns NULL if there's no match. the returned event is what's actually found in the system, and it must be freed with tracefs_dynevent_free(). also β if system is NULL, then the first event of any system of the given type that has the name of event gets returned. lowkey convenient. π«Ά
tracefs_dynevent_get_all() β allocates and returns an array of pointers to dynamic events of the given types that exist in the system. the last element of the array is a NULL pointer (so it's null-terminated, cute). the array must be freed with tracefs_dynevent_list_free(). if there are no events, a NULL pointer is returned. the types parameter is a type of one specific dynamic event, or a bitmask of dynamic event types tracefs_dynevent_type, that will be retrieved. if types is 0, dynamic events from all types will be retrieved. π
tracefs_dynevent_free() β frees a dynamic event context devent. no thoughts, just free it. π§
tracefs_dynevent_list_free() β frees an array of pointers to dynamic events, the one you got back from the tracefs_dynevent_get_all() API. bestie please actually call this, memory leaks are not the vibe π
tracefs_dynevent_info() β returns the type and the information of a given dynamic event dynevent. if any of the system, event, prefix, addr or format arguments are not NULL, then strings are allocated and returned back via these arguments:
systemandeventhold the system and the name of the dynamic event.- if
prefixis non-NULL, it holds an allocated string that holds the prefix portion of the dynamic event (the content up to the":", excluding it). - if
addris non-NULL, it holds the address or function that the dynamic event is attached to, if that's relevant for this event type. - if
formatis non-NULL, it holds the format string of the dynamic event.
β οΈ not me reminding you again: the content ingroup,event,prefix,addr, andformatmust be freed withfree(3)if they are set. like, all of it. every string. don't make me say it a third time π
tracefs_dynevent_get_event() β returns a tep event describing the given dynamic event. the API detects any newly created or removed dynamic events (it's literally keeping up with the tea β). the returned pointer to the tep event is controlled by @tep and must not be freed. hands off, bestie. π₯
π RETURN VALUE π
tracefs_dynevent_create() returns 0 on success, or -1 on error. if a parsing error occurs, then tracefs_error_last(3) may be used to retrieve the error message explaining the parsing issue. iykyk. π
tracefs_dynevent_destroy() and tracefs_dynevent_destroy_all() return 0 on success, or -1 on error. if force is enabled, the functions may fail on disabling the events. so like, force isn't magic, bestie. π
tracefs_dynevent_get() returns an allocated dynamic event from the system that matches the type, system and event you gave it. π
tracefs_dynevent_get_all() returns an allocated array of pointers to dynamic events, or NULL in case of an error or in case there are no events in the system. that array must be freed by tracefs_dynevent_list_free(). β¨
tracefs_dynevent_info() returns the type of the given dynamic event, or TRACEFS_DYNEVENT_UNKNOWN on error. if system, event, prefix, addr, or format are non-NULL, they will contain allocated strings that must be freed by free(3). π«Ά
tracefs_dynevent_get_event() returns a pointer to a tep event, or NULL in case of an error or if the requested dynamic event is missing. the returned pointer to the tep event is controlled by @tep and must not be freed. (yes, again. it's that serious. β)
π ERRORS π
the following errors are for all the above calls:
ENODEVβ dynamic events of the requested type are not configured for the running kernel. like the kernel said "no β€οΈ".ENOMEMβ memory allocation error. tragic. π
tracefs_dynevent_create() can fail with the following error:
EINVALβ most likely a parsing error occurred (usetracefs_error_last(3)to possibly see what that error was).
also, other errors may happen too, caused by internal system calls. so like, stay humble π€·ββοΈ
π§ EXAMPLE π§
bestie this one's long but it's SO good β it's a whole kprobe + kretprobe setup watching do_sys_openat2 in a child process. look at her go β¨
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <tracefs.h>
static struct tep_event *open_event;
static struct tep_format_field *file_field;
static struct tep_event *openret_event;
static struct tep_format_field *ret_field;
static int callback(struct tep_event *event, struct tep_record *record,
int cpu, void *data)
{
struct trace_seq seq;
trace_seq_init(&seq);
tep_print_event(event->tep, &seq, record, "%d-%s: ", TEP_PRINT_PID, TEP_PRINT_COMM);
if (event->id == open_event->id) {
trace_seq_puts(&seq, "open file='");
tep_print_field(&seq, record->data, file_field);
trace_seq_puts(&seq, "'\n");
} else if (event->id == openret_event->id) {
unsigned long long ret;
tep_read_number_field(ret_field, record->data, &ret);
trace_seq_printf(&seq, "open ret=%lld\n", ret);
} else {
goto out;
}
trace_seq_terminate(&seq);
trace_seq_do_printf(&seq);
out:
trace_seq_destroy(&seq);
return 0;
}
static pid_t run_exec(char **argv, char **env)
{
pid_t pid;
pid = fork();
if (pid)
return pid;
execve(argv[0], argv, env);
perror("exec");
exit(-1);
}
const char *mykprobe = "my_kprobes";
int main (int argc, char **argv, char **env)
{
struct tracefs_dynevent *kprobe, *kretprobe;
const char *sysnames[] = { mykprobe, NULL };
struct tracefs_instance *instance;
struct tep_handle *tep;
pid_t pid;
if (argc < 2) {
printf("usage: %s command\n", argv[0]);
exit(-1);
}
instance = tracefs_instance_create("exec_open");
if (!instance) {
perror("creating instance");
exit(-1);
}
tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
kprobe = tracefs_kprobe_alloc(mykprobe, "open", "do_sys_openat2",
"file=+0($arg2):ustring flags=+0($arg3):x64 mode=+8($arg3):x64\n");
kretprobe = tracefs_kretprobe_alloc(mykprobe, "openret", "do_sys_openat2", "ret=%ax", 0);
if (!kprobe || !kretprobe) {
perror("allocating dynamic events");
exit(-1);
}
if (tracefs_dynevent_create(kprobe) || tracefs_dynevent_create(kretprobe)){
char *err = tracefs_error_last(NULL);
perror("Failed to create kprobes:");
if (err && strlen(err))
fprintf(stderr, "%s\n", err);
exit(-1);
}
tep = tracefs_local_events_system(NULL, sysnames);
if (!tep) {
perror("reading events");
exit(-1);
}
open_event = tep_find_event_by_name(tep, mykprobe, "open");
file_field = tep_find_field(open_event, "file");
openret_event = tep_find_event_by_name(tep, mykprobe, "openret");
ret_field = tep_find_field(openret_event, "ret");
tracefs_event_enable(instance, mykprobe, NULL);
pid = run_exec(&argv[1], env);
/* Let the child start to run */
sched_yield();
do {
tracefs_load_cmdlines(NULL, tep);
tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
} while (waitpid(pid, NULL, WNOHANG) != pid);
/* Will disable the events */
tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
tracefs_dynevent_free(kprobe);
tracefs_dynevent_free(kretprobe);
tracefs_instance_destroy(instance);
tep_free(tep);
return 0;
}
π FILES π
tracefs.hβ header file to include in order to have access to the library APIs. yes bestie, you gotta#includeit, that's the whole point π-ltracefsβ linker switch to add when building a program that uses the library. don't forget it or the linker will simply pass away π
β SEE ALSO β
libtracefs(3), libtraceevent(3), trace-cmd(1) β go read the whole friend group π«Ά
π AUTHOR π
- Steven Rostedt
<rostedt@goodmis.org[1]> - Tzvetomir Stoyanov
<tz.stoyanov@gmail.com[2]> - Yordan Karadzhov
<y.karadz@gmail.com[3]>
like, legends only. thank you for your service π
π REPORTING BUGS π
Report bugs to <linux-trace-devel@vger.kernel.org[4]> β bestie please tell them, they genuinely want to know, no gatekeeping π
β¨ okay that's part 1 done, more coming, don't go anywhere β¨
π LICENSE π
okay so legal stuff, but make it cute: libtracefs is Free Software licensed under the GNU LGPL 2.1. slay, freedom, etc. ποΈ
π RESOURCES π
where to find the source, bestie β the repo lives right here:
https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
bookmark it, screenshot it, tattoo it on your forearm, idc, just know it π«Ά
π COPYING π
Copyright (C) 2021 VMware, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). periodt β that's the whole vibe, no notes π
π¦ NOTES π¦
the footnotes!! the little contact list of legends who make this happen β¨
rostedt@goodmis.orgβ [mailto:rostedt@goodmis.org](mailto:rostedt@goodmis.org)tz.stoyanov@gmail.comβ [mailto:tz.stoyanov@gmail.com](mailto:tz.stoyanov@gmail.com)y.karadz@gmail.comβ [mailto:y.karadz@gmail.com](mailto:y.karadz@gmail.com)linux-trace-devel@vger.kernel.orgβ [mailto:linux-trace-devel@vger.kernel.org](mailto:linux-trace-devel@vger.kernel.org)
like, these are the humans to ping. be normal about it, be nice about it, they're doing amazing work π
okay that's literally the whole page bestie, you made it to the end π this was libtracefs 1.8.4, dated 10/21/2026 β go read it again and go write some tracing code, you've got this π β¨πΈ
π¦ where this page comes from
- arch rolling: libtracefs 1.8.4-1 Β· GPL-2.0-only AND LGPL-2.1-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms