LIBTRACEFS(3)

πŸ’… LIBTRACEFS(3) β€” the libtracefs manual, bestie edition

okay so this is the libtracefs manual page, and today we're learning about user probes. stay with me, it's actually so good.


✨ NAME ✨

tracefs_uprobe_alloc, tracefs_uretprobe_alloc β€” allocate a brand new user (return) probe 🫢


🌸 SYNOPSIS 🌸

#include <tracefs.h>

struct tracefs_dynevent *
tracefs_uprobe_alloc(const char *system, const char *event,
                     const char *file, unsigned long long offset, const char *fetchargs)
struct tracefs_dynevent *
tracefs_uretprobe_alloc(const char *system, const char *event,
                     const char *file, unsigned long long offset, const char *fetchargs)

πŸ’– DESCRIPTION πŸ’–

So tracefs_uprobe_alloc() literally allocates a new uprobe context, no cap. It's gonna live in the system group β€” or in uprobes if system is NULL β€” and it gets the name event. The uprobe is attached to offset inside file. The list of arguments you describe in fetchargs gets fetched with the uprobe. πŸŽ€

The pointer you get back to the user probe context must be freed with tracefs_dynevent_free(). also important: the uprobe is not configured in the system just by allocating it β€” you use the tracefs_dynevent_* family of APIs to actually configure it. iykyk.

tracefs_uretprobe_alloc() behaves exactly the same as tracefs_uprobe_alloc(), the only difference being that it allocates a context for a user return probe (a uretprobe). same vibes, different return. πŸ¦‹


⭐ RETURN VALUE ⭐

tracefs_uprobe_alloc() and tracefs_uretprobe_alloc() return a pointer to an allocated tracefs_dynevent structure that describes the user probe. That pointer must be freed with tracefs_dynevent_free(3). πŸ“Œ

heads up bestie: this only allocates a descriptor representing the uprobe. it does not modify the running system. on error, NULL is returned. periodt.

πŸ”₯ EXAMPLE πŸ”₯

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#include <tracefs.h>

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: %s",
                        TEP_PRINT_PID, TEP_PRINT_COMM, TEP_PRINT_NAME);
        trace_seq_puts(&seq, "'\n");

        trace_seq_terminate(&seq);
        trace_seq_do_printf(&seq);
        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 *myprobe = "my_urobes";

int main (int argc, char **argv, char **env)
{
        struct tracefs_dynevent *uprobe, *uretprobe;
        struct tep_handle *tep;
        struct tracefs_instance *instance;
        const char *sysnames[] = { myprobe, NULL };
        long addr;
        pid_t pid;

        if (argc < 3) {
                printf("usage: %s file_offset command\n", argv[0]);
                exit(-1);
        }
        addr = strtol(argv[1], NULL, 0);

        instance = tracefs_instance_create("exec_open");
        if (!instance) {
                perror("creating instance");
                exit(-1);
        }

        tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_UPROBE|TRACEFS_DYNEVENT_URETPROBE, true);

        uprobe = tracefs_uprobe_alloc(myprobe, "user_probe", argv[2], addr, NULL);
        uretprobe = tracefs_uretprobe_alloc(myprobe, "user_retprobe", argv[2], addr, NULL);
        if (!uprobe || !uretprobe) {
                perror("allocating user probes");
                exit(-1);
        }

        if (tracefs_dynevent_create(uprobe) ||
            tracefs_dynevent_create(uretprobe)) {
                perror("creating user probes");
                exit(-1);
        }

        tep = tracefs_local_events_system(NULL, sysnames);
        if (!tep) {
                perror("reading events");
                exit(-1);
        }

        tracefs_event_enable(instance, myprobe, "user_probe");
        tracefs_event_enable(instance, myprobe, "user_retprobe");

        pid = run_exec(&argv[2], 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);

        /* disable and destroy the events */
        tracefs_dynevent_destroy(uprobe, true);
        tracefs_dynevent_destroy(uretprobe, true);
        tracefs_dynevent_free(uprobe);
        tracefs_dynevent_free(uretprobe);
        tracefs_instance_destroy(instance);

        return 0;
}

like, it's giving full working demo. ate. πŸ‘‘


πŸŽ€ FILES πŸŽ€

  • tracefs.h β€” the header file to include so you get access to the library APIs. literally required, bestie.
  • -ltracefs β€” the linker switch you add when building a program that uses the library.

🌈 SEE ALSO 🌈

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


πŸ‘‘ AUTHOR πŸ‘‘

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

πŸ› REPORTING BUGS πŸ›

Found a bug? report it to <linux-trace-devel@vger.kernel.org[3]> πŸ’Œ


πŸ“œ 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) 2022 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. linux-trace-devel@vger.kernel.org mailto:linux-trace-devel@vger.kernel.org

okay that's the whole page, you're so welcome πŸ’œ this was libtracefs version 1.8.4, dated 10/21/2026, and it's still called LIBTRACEFS(3) if you were wondering. go forth and probe some users, bestie 🌸

πŸ“¦ where this page comes from