Operations 9 min read

How to Dynamically Trace Kernel Functions with eBPF Using Last Branch Record

Last Branch Record (LBR) is a CPU‑level feature that records branch jumps; the Linux kernel’s bpf_get_branch_snapshot helper (since 5.16) enables eBPF programs to capture LBR data, and the bpflbr tool demonstrates how to trace kernel functions and bpf program execution, disassemble code, and output call stacks.

Linux Kernel Journey
Linux Kernel Journey
Linux Kernel Journey
How to Dynamically Trace Kernel Functions with eBPF Using Last Branch Record

Last Branch Record (LBR) is a CPU hardware feature that records branch‑jump information during program execution. Intel, AMD and, in progress, ARM CPUs support LBR.

On an Intel PC LBR support can be verified with:

$ dmesg | grep -i lbr
[    0.101727] Performance Events: XSAVE Architectural LBR, PEBS fmt4+-baseline,  AnyThread deprecated, Alderlake Hybrid events, 32-deep LBR, full-width counters, Intel PMU driver.

Kernel documentation and the Intel® 64 and IA‑32 Architectures Software Developer Manuals, together with LWN articles “An introduction to last branch records” and “Advanced usage of last branch records”, provide further details.

bpflbr architecture

The commit bpf: Introduce helper bpf_get_branch_snapshot (Linux 5.16) added the bpf_get_branch_snapshot helper, which retrieves the current CPU’s LBR records. The bpflbr tool implements this helper in an eBPF program and provides a userspace component to decode the records. It consists of two parts: bpflbr BPF program that collects LBR records. bpflbr userspace program that parses the records.

Because LBR records branch jumps, the BPF program must call bpf_get_branch_snapshot at its very beginning to capture the LBR snapshot before the program itself generates additional branch entries.

struct event {
    struct perf_branch_entry lbr[MAX_LBR_ENTRIES];
    // ...
} __attribute__((packed));

static __always_inline int emit_lbr_event(void *ctx) {
    struct event *event;
    // ...
    if (!cfg->suppress_lbr)
        event->nr_bytes = bpf_get_branch_snapshot(event->lbr, sizeof(event->lbr), 0);
    bpf_ringbuf_output(&events, event, sizeof(*event), 0);
    return BPF_OK;
}

Userspace decoding

If the record refers to a kernel function, resolve the symbol name via /proc/kallsyms and obtain line information from the vmlinux debug symbols.

If the record refers to a BPF program, pre‑parse all BPF program metadata (function names, line info) and map the LBR entries to those symbols.

Output the branch‑jump information as a call stack.

Feature list

Dynamic tracing of BPF program internals using fexit.

Dynamic tracing of kernel function branch records before BPF execution using fentry.

Output of function call stacks via --output-stack.

Detailed output of function parameters and return values.

Disassembly of kernel functions and BPF programs using the Capstone engine.

Command‑line options

$ ./bpflbr -h
Usage of bpflbr:
  -d, --disasm               disasm bpf prog or kernel function
  -B, --disasm-bytes uint    disasm bytes of kernel function, 0 to guess automatically
      --disasm-intel-syntax  use Intel asm syntax for disasm, ATT syntax by default
      --filter-pid uint32    filter pid for tracing
  -k, --kfunc strings        filter kernel functions by shell wildcards
      --kfunc-all-kmods      filter functions in all kernel modules
      --limit-events uint    limited number events to output, 0 to output all events
  -m, --mode string          mode of lbr tracing, exit or entry (default "exit")
  -o, --output string        output file for the result, default is stdout
      --output-stack         output function call stack
  -p, --prog strings         bpf prog info for bpflbr in format PROG[,PROG,..]
      --suppress-lbr         suppress LBR perf event
  -v, --verbose              output verbose log

Demonstrations

bpflbr pwru - exit mode
bpflbr pwru - exit mode
bpflbr pwru - entry mode
bpflbr pwru - entry mode
bpflbr func stack
bpflbr func stack
bpflbr disasm bpf prog
bpflbr disasm bpf prog
bpflbr disasm kfunc
bpflbr disasm kfunc

Future work

Planned extensions include integrating dynamic function‑parameter filtering from the “eBPF Talk: Dynamic Filtering of Function Parameters” talk, adding pcap‑filter(7) syntax for packet filtering, and referencing the bice project to selectively output fields of struct/union pointer arguments.

Summary

bpflbr

leverages the CPU’s LBR feature to trace kernel functions and BPF programs, providing call‑stack output, detailed argument/return information, and disassembly of both kernel and BPF code.

References

Intel® 64 and IA‑32 Architectures Software Developer Manuals – https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html

An introduction to last branch records – https://lwn.net/Articles/680985/

Advanced usage of last branch records – https://lwn.net/Articles/680996/

bpf: Introduce helper bpf_get_branch_snapshot – https://github.com/torvalds/linux/commit/856c02dbce4f

Capstone engine – https://github.com/capstone-engine/capstone

bice – https://github.com/leonhwangprojects/bice

bpflbr source repository – https://github.com/Asphaltt/bpflbr

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LinuxeBPFkernel tracingbpf_get_branch_snapshotbpflbrLast Branch Record
Linux Kernel Journey
Written by

Linux Kernel Journey

Linux Kernel Journey

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.