LIBTRACEEVENT(3)

πŸ’– LIBTRACEEVENT πŸ’–

okay bestie, welcome to the libtraceevent Manual ✨ β€” this is page LIBTRACEEVENT(3) and it's genuinely a niche little corner of the trace world, but we're gonna make it make sense fr fr 🫢


🌸 NAME 🌸

tep_plugin_kvm_get_func, tep_plugin_kvm_put_func β€” Add function name for instruction pointer of kvm plugin πŸ’…

✨ SYNOPSIS ✨

#include <event-parse.h>

const char *tep_plugin_kvm_get_func(struct tep_event *event,
                                    struct tep_record *record,
                                    unsigned long long *paddr);
void tep_plugin_kvm_put_func(const char *func);

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so, PLOT TWIST, the functions tep_plugin_kvm_get_func() and tep_plugin_kvm_put_func() are not to be called by an application, but instead are to be defined by an application. yes you read that right, it's giving reverse energy πŸ‘‘

Certain events (like kvm_exit and kvm_entry) have the instruction pointer of where in the guest the context changed from guest to host. As the host only knows the instruction pointer and does not have information about what function in the guest that instruction pointer belongs to, it can only print the address. like, the host is out here reading a name tag that's just a bunch of hex digits πŸ’€

But the application may have more information about the guest, and know where the guest was when the exit occurred, and also even know the function name of that address. main character info, honestly 🌟

The KVM plugin for libtraceevent is called on these events, and then calls tep_plugin_kvm_get_func() to see if that function can resolve the instruction pointer address to a real function name. If the return is non NULL, it will print the function in the output for that event. slay.

These functions are currently defined as weak functions within the plugin, as to not require them to be defined elsewhere. For an application to override the weak function, it will need to define the function in a file that gets compiled with -rdynamic. That will tell the dynamic linker to examine that object file and use function names to resolve weak functions in other shared objects (in this case the KVM plugin shared object). iykyk 🧚

If the application defines tep_plugin_kvm_get_func(), it must use the above prototype. The event will hold the KVM event that has the instruction pointer field. The record will be the instance of that event. The application's function does not need to use these parameters, but they may be useful for finding the function name for the address. The paddr is a pointer to a 64 bit value (where only 32 bits may be used on 32 bit machines). This value is the instruction pointer to look up. If the application knows the start address of the function as well, it can set paddr to that address, and the KVM plugin will also append a "+offset" to the function name where the offset is the original value in paddr minus the value in paddr when it is called. Finally, the application should return the function name as a nul terminated string if one is found. periodt πŸ’…

If the returned string of tep_plugin_kvm_get_func() was allocated, the KVM plugin will call tep_plugin_kvm_put_func() when it is through with it, passing the value returned by tep_plugin_kvm_get_func() as func. This allows the application to free it if necessary. clean up after yourself queen πŸŽ€


πŸ’œ RETURN VALUE πŸ’œ

The tep_plugin_kvm_get_func() is not to be called by the application but instead is to be defined by the application. It should return a nul terminated string representing the function for the given instruction pointer passed to it by reference in paddr.

It can then optionally update the paddr to a value that holds the start of the function. The string returned may be freed by the tep_plugin_kvm_put_func() that the application should define to clean up the string.

⚠️ heads up bestie: the below example needs to be compiled with the -rdynamic flag so that the dynamic linker can resolve the tep_plugin_kvm_get_func() and tep_plugin_kvm_put_func() functions. no flag = no vibes.

When run against a trace.dat file produced by trace-cmd(1) recording the kvm_exit and kvm_entry events on a guest, and then the guest's /proc/kallsyms file is passed as the second parameter, the output produced will look something like:

    CPU 0/KVM-20407 83156.177626 [000] kvm_exit     reason APIC_ACCESS rip 0xffffffffb0056ee2 exit native_apic_mem_write+0x2 info 10b0 0
    CPU 0/KVM-20407 83156.177632 [000] kvm_entry     vcpu 0 rip 0xffffffffb0056ee8 enter native_apic_mem_write+0x8

But without those callbacks, it would look like:

    CPU 0/KVM-20407 83156.177626 [000] kvm_exit     reason APIC_ACCESS rip 0xffffffffb0056ee2 info 10b0 0
    CPU 0/KVM-20407 83156.177632 [000] kvm_entry     vcpu 0 rip 0xffffffffb0056ee8

look at that little exit native_apic_mem_write+0x2 glow-up ✨ the callbacks ATE πŸ”₯


🌈 EXAMPLE 🌈

    #include <stdio.h>
    #include <stdlib.h>
    #include <event-parse.h>
    #include <trace-cmd.h>
    #include <sys/stat.h>

    static struct tep_handle *tep;

    const char *tep_plugin_kvm_get_func(struct tep_event *event, struct tep_record *record,
                                        unsigned long long *paddr)
    {
            const char *func;
            char *event_func;
            char *ename;

            func = tep_find_function(tep, *paddr);
            if (!func)
                    return NULL;

            if (strcmp(event->name, "kvm_exit") == 0)
                    ename = "exit";
            else
                    ename = "enter";

            /*
             * Normally, passing back func directly is sufficient and then
             * tep_plugin_kvm_put_func() would not be required. But this example
             * is showing how to handle allocation of the returned string.
             */
            event_func = malloc(strlen(ename) + strlen(func) + 2);
            if (!event_func)
                    return NULL;
            sprintf(event_func, "%s %s", ename, func);

            *paddr = tep_find_function_address(tep, *paddr);

            return event_func;
    }

    void tep_plugin_kvm_put_func(const char *func)
    {
            char *f = (char *)func;

            free(f);
    }

    static int show_event(struct tracecmd_input *handle, struct tep_event *event,
                          struct tep_record *record, int cpu, void *data)
    {
            static struct trace_seq seq;
            tep = data;

            if (!seq.buffer)
                    trace_seq_init(&seq);

            trace_seq_reset(&seq);
            tep_print_event(tracecmd_get_tep(handle), &seq, record,
                            "%s-%d\t%6.1000d [%03d] %s\t%s\n",
                            TEP_PRINT_COMM, TEP_PRINT_PID,
                            TEP_PRINT_TIME, TEP_PRINT_CPU,
                            TEP_PRINT_NAME, TEP_PRINT_INFO);
            trace_seq_terminate(&seq);
            trace_seq_do_printf(&seq);
            return 0;
    }

    int main(int argc, char **argv)
    {
            struct tracecmd_input *handle;
            struct tep_handle *guest_tep;
            struct stat st;
            FILE *fp;
            char *buf;

            if (argc < 3) {
                    printf("usage: trace.dat guest_kallsyms_file\n");
                    exit(-1);
            }

            handle = tracecmd_open(argv[1], 0);
            if (!handle) {
                    perror(argv[1]);
                    exit(-1);
            }

            /* Just for kallsyms parsing */
            guest_tep = tep_alloc();
            if (!guest_tep)
                    exit(-1);

            if (stat(argv[2], &st) < 0) {
                    perror(argv[2]);
                    exit(-1);
            }

            buf = malloc(st.st_size + 1);
            if (!buf)
                    exit(-1);

            fp = fopen(argv[2], "r");
            if (!fp) {
                    perror(argv[2]);
                    exit(-1);
            }

            if (fread(buf, st.st_size, 1, fp) < 0) {
                    perror(argv[2]);
                    exit(-1);
            }

            buf[st.st_size] = '\0';

            if (tep_parse_kallsyms(guest_tep, buf) < 0) {
                    printf("Failed to parse %s\n", argv[2]);
                    exit(-1);
            }
            free(buf);

            tracecmd_follow_event(handle, "kvm", "kvm_exit", show_event, guest_tep);
            tracecmd_follow_event(handle, "kvm", "kvm_entry", show_event, guest_tep);

            tracecmd_iterate_events(handle, NULL, 0, NULL, NULL);

            tep_free(guest_tep);
            tracecmd_close(handle);
    }

πŸŽ€ FILES πŸŽ€

  • event-parse.h β€” Header file to include in order to have access to the library APIs. your VIP pass fr.
  • -ltraceevent β€” Linker switch to add when building a program that uses the library. don't forget to link her πŸ’…

⭐ SEE ALSO ⭐

libtraceevent(3), trace-cmd(1)

πŸ’€ REPORTING BUGS πŸ’€

Report bugs to <linux-trace-devel@vger.kernel.org[1]>

ngl if something's broken, tell the list, they wanna know 🫢

πŸ“œ LICENSE πŸ“œ

libtraceevent is Free Software licensed under the GNU LGPL 2.1

🌐 RESOURCES 🌐

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

πŸ“ NOTES πŸ“

  1. linux-trace-devel@vger.kernel.org β€” mailto:linux-trace-devel@vger.kernel.org

and that's the whole page bestie, no crumbs left πŸ’– this was libtraceevent 1.9.0, dated 06/15/2026, and it was page LIBTRACEEVENT(3) the whole time ✨ periodt, go forth and resolve those instruction pointers πŸ‘‘πŸ¦‹

πŸ“¦ where this page comes from