LIBTRACEFS(3)

πŸ’… LIBTRACEFS(3) β€” kprobes, kretprobes & main character energy ✨

okay so this is a page from the libtracefs Manual, and bestie, it's all about making kprobes happen. sit down, hydrate, let's go 🫢


🌸 NAME 🌸

tracefs_kprobe_alloc, tracefs_kretprobe_alloc, tracefs_kprobe_raw, tracefs_kretprobe_raw, tracefs_kprobe_destroy β€” the whole friend group tbh: allocate, get, create, and remove kprobes πŸ’–


✨ SYNOPSIS ✨

#include <tracefs.h>

struct tracefs_dynevent *
tracefs_kprobe_alloc(const char *system, const char *event,
                       const char *addr, const char *format);
struct tracefs_dynevent *
tracefs_kretprobe_alloc(const char *system, const char *event,
                          const char *addr, const char *format, unsigned int max);
int tracefs_kprobe_raw(const char *system, const char *event,
                         const char *addr, const char *format);
int tracefs_kretprobe_raw(const char *system, const char *event,
                            const char *addr, const char *format);
int tracefs_kprobe_destroy(const char *system, const char *event,
                           const char *addr, const char *format, bool force);

πŸ¦‹ DESCRIPTION πŸ¦‹

the alloc girlies πŸ‘‘

tracefs_kprobe_alloc() allocates a brand-new kprobe context. and like… it does not configure the kprobe in the system. it's giving "dressed up, nowhere to go" πŸ’€. to actually put it in the system you pass the returned descriptor into tracefs_dynevent_create(3). the new kprobe will land in the system group (or kprobes if system is NULL) and get the name of event (or addr if event is NULL). the kprobe gets inserted at addr β€” that's a function name, with or without an offset, or a plain address β€” and format defines the format of the kprobe. for the deep lore, go read the Linux documentation file under: Documentation/trace/kprobetrace.rst πŸ“š

tracefs_kretprobe_alloc() is literally the same as tracefs_kprobe_alloc, except it allocates a context for a kretprobe. it has one extra parameter, which is optional: max β€” the maxactive count. kretprobe deets live in Documentation/trace/kprobetrace.rst too. no cap 🧚

the raw ones πŸ”₯

tracefs_kprobe_raw() will create a kprobe event, for real for real. if system is NULL, the default "kprobes" is used for the group (the event system). otherwise, if system is specified, the kprobe gets created under the group with that name. event is the name of the kprobe event to create. addr can be a function, a function and offset, or a kernel address β€” that's where the kprobe gets inserted in the kernel. and format is the kprobe format as specified as FETCHARGS in the Linux kernel source in the Documentation/trace/kprobetrace.rst document. iconic behavior 😌

tracefs_kretprobe_raw() is the same as tracefs_kprobe_raw(), except β€” plot twist β€” it creates a kretprobe instead of a kprobe. the difference is also described in the Linux kernel source in the Documentation/trace/kprobetrace.rst file.

the destroyer πŸ’…

tracefs_kprobe_destroy() will destroy a specific kprobe or kretprobe created by tracefs_kprobe_raw() or tracefs_kretprobe_raw() β€” using the same parameters. same energy, reverse direction 🫢


⭐ RETURN VALUE ⭐

tracefs_kprobe_raw() and tracefs_kretprobe_raw() return 0 on success, or -1 on error. and if a parsing error happens on tracefs_kprobe_raw() or tracefs_kretprobe_raw(), you can hit up tracefs_error_last(3) to retrieve the error message explaining the parsing issue. she'll tell you what went wrong πŸ’–

tracefs_kprobe_alloc() and tracefs_kretprobe_alloc() return a pointer to an allocated tracefs_dynevent structure describing the probe. that pointer must be freed by tracefs_dynevent_free(3). note: this only allocates a descriptor representing the kprobe β€” it does not modify the running system. it's a vibe, not a syscall 😭

tracefs_kprobe_destroy() returns 0 on success, or -1 on error if it was not able to successfully destroy (or find) the kprobe or kretprobe. rough out here πŸ’€


πŸ“ ERRORS πŸ“

the following errors are for all the above calls:

  • EPERM β€” not run as root user πŸ™ˆ
  • ENODEV β€” kprobe events are not configured for the running kernel
  • ENOMEM β€” memory allocation error

tracefs_kprobe_raw(), tracefs_kretprobe_raw(), tracefs_kprobe_alloc(), and tracefs_kretprobe_alloc() can fail with the following errors too:

  • EBADMSG β€” if addr is NULL
  • EINVAL β€” most likely a parsing error occurred (use tracefs_error_last(3) to possibly see what that error was)

other errors may also happen, caused by internal system calls. yeah, the kernel has layers πŸ§…


🌈 EXAMPLE 🌈

this one's a whole meal, bestie. it's all verbatim, don't touch it 🍽️
    #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";

    enum kprobe_type {
            KPROBE,
            KRETPROBE,
    };

    static void __kprobe_create(enum kprobe_type type, const char *event,
                                const char *addr, const char *fmt)
    {
            char *err;
            int r;

            if (type == KPROBE)
                    r = tracefs_kprobe_raw(mykprobe, event, addr, fmt);
            else
                    r = tracefs_kretprobe_raw(mykprobe, event, addr, fmt);
            if (r < 0) {
                    err = tracefs_error_last(NULL);
                    perror("Failed to create kprobe:");
                    if (err && strlen(err))
                            fprintf(stderr, "%s\n", err);
            }
    }

    static void kprobe_create(const char *event, const char *addr,
                              const char *fmt)
    {
            __kprobe_create(KPROBE, event, addr, fmt);
    }

    static void kretprobe_create(const char *event, const char *addr,
                                 const char *fmt)
    {
            __kprobe_create(KRETPROBE, event, addr, fmt);
    }

    int main (int argc, char **argv, char **env)
    {
            struct tracefs_instance *instance;
            struct tep_handle *tep;
            const char *sysnames[] = { mykprobe, NULL };
            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_create("open", "do_sys_openat2",
                          "file=+0($arg2):ustring flags=+0($arg3):x64 mode=+8($arg3):x64\n");

            kretprobe_create("openret", "do_sys_openat2", "ret=%ax");

            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_instance_destroy(instance);
            tep_free(tep);

            return 0;
    }

πŸŽ€ FILES πŸŽ€

  • tracefs.h β€” header file to include in order to have access to the library APIs
  • -ltracefs β€” linker switch to add when building a program that uses the library. don't forget her, she's the plug πŸ”Œ

πŸ‘‘ SEE ALSO πŸ‘‘

libtracefs(3), libtraceevent(3), trace-cmd(1)


πŸ’œ AUTHOR πŸ’œ

  • Steven Rostedt <rostedt@goodmis.org[1]>
  • Tzvetomir Stoyanov <tz.stoyanov@gmail.com[2]>
  • sameeruddin shaik <sameeruddin.shaik8@gmail.com[3]>

πŸ”₯ REPORTING BUGS πŸ”₯

report bugs to <linux-trace-devel@vger.kernel.org[4]> β€” they want to hear from you, fr fr 🫢


🌸 LICENSE 🌸

libtracefs is Free Software licensed under the GNU LGPL 2.1.


🧚 RESOURCES 🧚

https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/


⭐ COPYING ⭐

Copyright (C) 2021 VMware, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL).


πŸ¦‹ NOTES πŸ¦‹

  1. rostedt@goodmis.org mailto:rostedt@goodmis.org
  2. tz.stoyanov@gmail.com mailto:tz.stoyanov@gmail.com
  3. sameeruddin.shaik8@gmail.com mailto:sameeruddin.shaik8@gmail.com
  4. linux-trace-devel@vger.kernel.org mailto:linux-trace-devel@vger.kernel.org

and that's the whole page, bestie πŸŽ‰ this was libtracefs version 1.8.4, dated 10/21/2026 β€” screenshot it, cite it, slay it πŸ’…βœ¨ periodt.

πŸ“¦ where this page comes from