Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

BPF and bpftrace

Introduction

BPF (Berkeley Packet Filter) has evolved from a simple packet filter into a powerful, general-purpose execution engine in the Linux kernel. Modern eBPF (extended BPF) enables safe, efficient, programmable tracing and observability without modifying kernel code or loading kernel modules.

bpftrace is a high-level tracing language built on eBPF, inspired by awk and DTrace. It enables powerful one-liners and custom scripts for tracing network, disk, scheduler, and application behavior.

This page covers BPF architecture, bpftrace syntax and one-liners, BPF map types, BCC tools, tracepoint usage, and real-world observability recipes.

BPF Architecture

graph TD
    subgraph "User Space"
        BT["bpftrace scripts"]
        LIB["libbpf / BCC"]
        APP["Custom BPF programs<br>(C + libbpf)"]
        BPFTOOL["bpftool<br>(management)"]
    end
    subgraph "Kernel Space - BPF Runtime"
        VER["BPF Verifier<br>━━━━━━━━━━━━<br>Safety checks:<br>• No infinite loops<br>• No null derefs<br>• No out-of-bounds<br>• All memory validated<br>• Program size limits"]
        JIT["BPF JIT Compiler<br>━━━━━━━━━━━━<br>Compiles bytecode<br>to native x86/ARM"]
        PROG["BPF Programs"]
        MAP["BPF Maps<br>━━━━━━━━━━━━<br>hash, array, ringbuf,<br>percpu, lru, ..."]
        HOOK["Hook Points<br>━━━━━━━━━━━━<br>kprobes, tracepoints,<br>XDP, perf_events,<br>fentry/fexit, LSM"]
    end
    BT --> LIB
    LIB --> VER
    APP --> VER
    BPFTOOL --> VER
    VER --> JIT --> PROG
    PROG --> MAP
    PROG --> HOOK

BPF Program Lifecycle

graph LR
    subgraph "1. Write"
        SRC["C source<br>or bpftrace"]
    end
    subgraph "2. Compile"
        CLANG["clang/LLVM<br>→ BPF bytecode"]
    end
    subgraph "3. Load"
        LOAD["bpf() syscall<br>loads into kernel"]
    end
    subgraph "4. Verify"
        CHECK["BPF verifier<br>safety analysis"]
    end
    subgraph "5. JIT"
        JIT2["Native code<br>generation"]
    end
    subgraph "6. Attach"
        ATTACH["Hook to kprobe,<br>tracepoint, XDP, etc."]
    end
    subgraph "7. Run"
        RUN["Execute on<br>every event"]
    end
    SRC --> CLANG --> LOAD --> CHECK --> JIT2 --> ATTACH --> RUN

BPF Program Types

TypeHook PointUse CaseKernel Version
kprobe / kretprobeKernel functionsFunction entry/exit tracing3.19+
tracepointStatic kernel tracepointsStructured event tracing4.7+
uprobe / uretprobeUser-space functionsApplication tracing4.18+
XDPNetwork driverHigh-performance packet processing4.8+
perf_eventPerformance countersCPU/memory profiling4.9+
raw_tracepointRaw tracepoint argsLow-overhead tracing4.17+
LSMLinux Security ModuleSecurity policy enforcement5.7+
fentry / fexitKernel functions (BTF)Fast function tracing5.5+
tp_btfBTF-aware tracepointsTyped tracepoint access5.6+
cgroup/sockcgroup socketNetwork policy4.12+
sched_*Scheduler eventsScheduling policy5.6+

BPF Maps

Maps are the primary data structure for BPF programs. They allow BPF programs to store and share data.

Map Types

TypeDescriptionUse Case
BPF_MAP_TYPE_HASHKey-value hash tableGeneral lookups, counters
BPF_MAP_TYPE_ARRAYFixed-size arrayPer-CPU stats, histograms
BPF_MAP_TYPE_PERCPU_HASHPer-CPU hash tableHigh-throughput counters
BPF_MAP_TYPE_PERCPU_ARRAYPer-CPU arrayPer-CPU statistics
BPF_MAP_TYPE_RINGBUFRing bufferEvent streaming to user space
BPF_MAP_TYPE_LRU_HASHLRU-evicting hashBounded-size caches
BPF_MAP_TYPE_STACK_TRACEStack trace storageProfiling, flame graphs
BPF_MAP_TYPE_QUEUEFIFO queueEvent buffering
BPF_MAP_TYPE_STACKLIFO stackState tracking
BPF_MAP_TYPE_DEVMAPDevice mapXDP packet forwarding
BPF_MAP_TYPE_CPUMAPCPU mapXDP CPU redirection

Map Operations in BPF C

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

// Define a hash map
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 10240);
    __type(key, u32);      // PID
    __type(value, u64);    // byte count
} bytes_by_pid SEC(".maps");

// Define a per-CPU array
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 256);
    __type(key, u32);
    __type(value, u64);
} per_cpu_stats SEC(".maps");

// Define a ring buffer for events
struct event {
    u32 pid;
    char comm[16];
    u64 bytes;
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} events SEC(".maps");

SEC("kprobe/vfs_read")
int trace_vfs_read(struct pt_regs *ctx)
{
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    u64 bytes = PT_REGS_PARM3(ctx);

    // Update hash map
    u64 *val = bpf_map_lookup_elem(&bytes_by_pid, &pid);
    if (val) {
        __sync_fetch_and_add(val, bytes);
    } else {
        bpf_map_update_elem(&bytes_by_pid, &pid, &bytes, BPF_ANY);
    }

    // Submit event to ring buffer
    struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (e) {
        e->pid = pid;
        bpf_get_current_comm(&e->comm, sizeof(e->comm));
        e->bytes = bytes;
        bpf_ringbuf_submit(e, 0);
    }

    return 0;
}

Map Management with bpftool

# List all maps
bpftool map list
# 1: hash  name bytes_by_pid  flags 0x0
#     key 4B  value 8B  max_entries 10240  memlock 270336B
# 2: percpu_array  name per_cpu_stats  flags 0x0
#     key 4B  value 8B  max_entries 256  memlock 65536B

# Dump map contents
bpftool map dump name bytes_by_pid
# key: 00 00 04 d2  value: 00 00 00 00 00 0f 42 40
# Found 1 element

# Read specific key
bpftool map lookup name bytes_by_pid key 1234

# Update map
bpftool map update name bytes_by_pid key 1234 value 0

# Pin map to filesystem (persistent)
bpftool map pin name bytes_by_pid /sys/fs/bpf/bytes_by_pid

# Show map memory usage
bpftool map show id 1

bpftrace Language Reference

One-Liners Cheat Sheet

System Calls

# Trace all open() syscalls with filename
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'

# Count syscalls by process
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'

# Syscall latency histogram
bpftrace -e '
tracepoint:raw_syscalls:sys_enter { @start[args->id] = nsecs; }
tracepoint:raw_syscalls:sys_exit /@start[args->id]/ {
    @usecs[comm] = hist((nsecs - @start[args->id]) / 1000);
    delete(@start[args->id]);
}'

# Count syscalls by type
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[args->id] = count(); }'

# Trace failed syscalls
bpftrace -e 'tracepoint:raw_syscalls:sys_exit /args->ret < 0/ {
    printf("%s[%d] syscall %d returned %d\n", comm, pid, args->id, args->ret);
}'

Disk I/O

# Block I/O latency histogram
bpftrace -e '
tracepoint:block:block_rq_issue { @start[args->dev] = nsecs; }
tracepoint:block:block_rq_complete /@start[args->dev]/ {
    @usecs[args->dev] = hist((nsecs - @start[args->dev]) / 1000);
    delete(@start[args->dev]);
}'

# I/O size distribution
bpftrace -e 'tracepoint:block:block_rq_issue { @bytes[args->rwbs] = hist(args->bytes / 1024); }'

# I/O by process
bpftrace -e 'tracepoint:block:block_rq_issue { @[comm, args->rwbs] = count(); }'

# Disk IOPS over time
bpftrace -e '
tracepoint:block:block_rq_complete { @iops = count(); }
interval:s:1 { print(@iops); clear(@iops); }'

# I/O throughput (KB/s) over time
bpftrace -e '
tracepoint:block:block_rq_complete { @kb = sum(args->nr_sector * 512 / 1024); }
interval:s:1 { print(@kb); clear(@kb); }'

Network

# TCP connections with destination
bpftrace -e 'kprobe:tcp_connect {
    $sk = (struct sock *)arg0;
    printf("%-16s %-6d -> %s:%d\n", comm, pid,
        ntop($sk->__sk_common.skc_daddr),
        ntohs($sk->__sk_common.skc_dport));
}'

# TCP retransmissions by process
bpftrace -e 'kprobe:tcp_retransmit_skb { @[comm, kstack] = count(); }'

# Packets per protocol
bpftrace -e 'tracepoint:net:netif_receive_skb {
    @proto[((struct sk_buff *)args->skb)->protocol] = count();
}'

# DNS queries (UDP port 53)
bpftrace -e 'kprobe:udp_sendmsg {
    $sk = (struct sock *)arg0;
    if ($sk->__sk_common.skc_dport == htons(53)) {
        printf("DNS query from %s (pid=%d)\n", comm, pid);
    }
}'

# TCP connection count by state
bpftrace -e 'kprobe:tcp_set_state { @state[args->newstate] = count(); }'

Scheduler

# Context switches by process
bpftrace -e 'tracepoint:sched:sched_switch { @[args->prev_comm] = count(); }'

# Scheduler latency (run queue wait time)
bpftrace -e '
tracepoint:sched:sched_wakeup { @qtime[args->pid] = nsecs; }
tracepoint:sched:sched_switch /@qtime[args->next_pid]/ {
    @usecs[args->next_comm] = hist((nsecs - @qtime[args->next_pid]) / 1000);
    delete(@qtime[args->next_pid]);
}'

# CPU time per process
bpftrace -e '
tracepoint:sched:sched_switch {
    @cputime[args->prev_comm] += nsecs - @oncpu[args->prev_pid];
    @oncpu[args->next_pid] = nsecs;
}'

# Off-CPU time (time spent sleeping/blocking)
bpftrace -e '
tracepoint:sched:sched_switch /args->prev_state != 0/ {
    @offcpu[args->prev_comm] = count();
}'

Memory

# Page allocation by process
bpftrace -e 'tracepoint:kmem:mm_page_alloc { @[comm, kstack] = count(); }'

# OOM kills
bpftrace -e 'tracepoint:oom:oom_score_adj_update {
    printf("OOM adj: pid=%d comm=%s score=%d\n", args->pid, comm, args->oom_score_adj);
}'

# Page cache activity
bpftrace -e 'tracepoint:filemap:mm_filemap_add_to_page_cache {
    @[comm] = count();
}'

# Kernel slab allocation sizes
bpftrace -e 'kprobe:__kmalloc { @sizes[comm] = hist(arg0); }'

bpftrace Script Syntax

#!/usr/bin/env bpftrace

// Variables
$x = 1;                          // Local variable
@y = 10;                         // Global variable (map)
@start[tid] = nsecs;             // Per-thread map
@[key] = count();                // Map with auto-key

// Probes
kprobe:func_name { }             // Kernel function entry
kretprobe:func_name { }          // Kernel function return
uprobe:/path/bin:func { }        // User function entry
uretprobe:/path/bin:func { }     // User function return
tracepoint:category:event { }    // Tracepoint
software:event:count { }         // Software events
hardware:event:count { }         // Hardware events
interval:s:N { }                 // Timer (every N seconds)
profile:hz:99 { }                // Sampling at 99 Hz
BEGIN { }                        // Script start
END { }                          // Script end

// Map operations
@[key] = count();                // Increment counter
@[key] = sum(value);             // Add to sum
@[key] = hist(value);            // Histogram
@[key] = lhist(value, min, max, step);  // Linear histogram
@[key] = min(value);             // Track minimum
@[key] = max(value);             // Track maximum
@[key] = avg(value);             // Track average
print(@map);                     // Print map
clear(@map);                     // Clear map
delete(@map[key]);               // Delete key

// Built-in variables
pid                              // Process ID
tid                              // Thread ID
uid                              // User ID
comm                             // Process name
nsecs                            // Nanoseconds since boot
elapsed                          // Nanoseconds since script start
kstack                           // Kernel stack trace
ustack                           // User stack trace
arg0..arg9                       // Function arguments
retval                           // Return value (kretprobe)
func                             // Function name
probe                            // Full probe name
curtask                          // Current task_struct pointer
cgroup                           // cgroup ID

// Built-in functions
printf("format", args...)        // Print formatted
time("format")                   // Print timestamp
str(ptr)                         // Read string from pointer
ntop(ip)                         // Convert IP to string
kaddr("symbol")                  // Kernel symbol address
uaddr("symbol")                  // User symbol address
reg("regname")                   // Read register
signal("SIGTERM")                // Send signal
system("cmd")                    // Run shell command
cat("/proc/...")                 // Read file
buf(ptr, len)                    // Read buffer

bpftrace Scripts

Disk Latency Heat Map

#!/usr/bin/env bpftrace
// disk-latency.bt — Disk I/O latency histogram

tracepoint:block:block_rq_issue {
    @start[args->dev, args->sector] = nsecs;
}

tracepoint:block:block_rq_complete /@start[args->dev, args->sector]/ {
    $latency = (nsecs - @start[args->dev, args->sector]) / 1000;
    @usecs = hist($latency);
    delete(@start[args->dev, args->sector]);
}

interval:s:1 {
    print(@usecs);
    clear(@usecs);
}

TCP Connection Tracer

#!/usr/bin/env bpftrace
// tcp-connect.bt — Trace TCP connections with latency

kprobe:tcp_connect {
    @start[pid] = nsecs;
    @pid_to_comm[pid] = comm;
}

kretprobe:tcp_connect /@start[pid]/ {
    $latency = (nsecs - @start[pid]) / 1000;
    printf("%-16s %-6d %d μs\n", @pid_to_comm[pid], pid, $latency);
    delete(@start[pid]);
    delete(@pid_to_comm[pid]);
}

Function Latency Tracer

#!/usr/bin/env bpftrace
// funclatency.bt — Trace function latency

kprobe:ext4_file_read { @start[tid] = nsecs; }
kretprobe:ext4_file_read /@start[tid]/ {
    @us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}

Process Execution Logger

#!/usr/bin/env bpftrace
// execsnoop.bt — Trace all process executions

tracepoint:syscalls:sys_enter_execve {
    printf("%-16s %-6d %s\n", comm, pid, str(args->filename));
}

Slow VFS Operations

#!/usr/bin/env bpftrace
// vfs-slow.bt — Trace VFS operations taking > 10ms

kprobe:vfs_read  { @start[tid, "read"]  = nsecs; }
kprobe:vfs_write { @start[tid, "write"] = nsecs; }
kprobe:vfs_open  { @start[tid, "open"]  = nsecs; }

kretprobe:vfs_read /@start[tid, "read"]/ {
    $dur = (nsecs - @start[tid, "read"]) / 1000000;
    if ($dur > 10) {
        printf("SLOW read: %s[%d] %d ms\n", comm, pid, $dur);
    }
    delete(@start[tid, "read"]);
}

kretprobe:vfs_write /@start[tid, "write"]/ {
    $dur = (nsecs - @start[tid, "write"]) / 1000000;
    if ($dur > 10) {
        printf("SLOW write: %s[%d] %d ms\n", comm, pid, $dur);
    }
    delete(@start[tid, "write"]);
}

kretprobe:vfs_open /@start[tid, "open"]/ {
    $dur = (nsecs - @start[tid, "open"]) / 1000000;
    if ($dur > 10) {
        printf("SLOW open: %s[%d] %d ms\n", comm, pid, $dur);
    }
    delete(@start[tid, "open"]);
}

BCC Tools

BCC (BPF Compiler Collection) provides ready-made BPF tools:

# Install BCC tools
apt install bpfcc-tools   # Debian/Ubuntu
yum install bcc-tools      # RHEL/CentOS

# ──── I/O Tools ────

# I/O latency histogram
biolatency-bpfcc
#      usecs          : count    distribution
#         0 -> 1      : 0       |                                        |
#         2 -> 3      : 1234    |*********                               |
#         4 -> 7      : 5678    |****************************************|

# I/O size distribution
bitesize-bpfcc

# Block I/O tracing
biosnoop-bpfcc
# TIME     COMM         PID   DISK  T  SECTOR     BYTES   LAT(ms)
# 0.000    mysqld       1234  sda   R  12345678   4096    0.12

# ──── Process Tools ────

# Trace new processes
execsnoop-bpfcc
# PCOMM            PID    PPID   RET ARGS
# ls               5678   1234     0 /bin/ls -la

# Trace file opens
opensnoop-bpfcc
# PID    COMM               FD ERR PATH
# 1234   nginx               5   0 /etc/nginx/nginx.conf

# Process exit events
exitsnoop-bpfcc
# PCOMM            PID    PPID   EXIT_CODE  DURATION(ms)

# ──── Network Tools ────

# TCP connection tracer
tcpconnect-bpfcc
# PID    COMM         IP SADDR            DADDR            DPORT
# 1234   curl         4  192.168.1.1      93.184.216.34    443

# TCP life (connections with duration)
tcplife-bpfcc
# PID    COMM         LADDR           LPORT RADDR           RPORT TX_KB RX_KB MS

# TCP retransmissions
tcpretrans-bpfcc
# PID    COMM         LADDR           LPORT RADDR           RPORT  STATE

# ──── Scheduler Tools ────

# Run queue latency
runqlat-bpfcc
#      usecs          : count    distribution
#         0 -> 1      : 5678    |****************************************|
#         2 -> 3      : 1234    |********                                |

# Off-CPU time
offcputime-bpfcc -f 10

# ──── Memory Tools ────

# Page cache hit rate
cachestat-bpfcc
# HITS   MISSES  DIRTIES HITRATIO   BUFFERS_MB  CACHED_MB
# 12345  123     456     99.01%     123         12345

# Memory slab allocator
slabratetop-bpfcc

# ──── CPU Profiling ────

# CPU profiling (for flame graphs)
profile-bpfcc -af 99 > out.stacks
# Then: flamegraph.pl out.stacks > flamegraph.svg

BPF Safety

The BPF verifier ensures programs are safe before they execute:

graph TD
    SRC["BPF Program Source<br>(C or bpftrace)"] --> VER["BPF Verifier"]
    VER --> C1["✓ No infinite loops<br>(bounded back-edges)"]
    VER --> C2["✓ No null pointer dereferences<br>(map lookup checks)"]
    VER --> C3["✓ No out-of-bounds access<br>(array bounds checking)"]
    VER --> C4["✓ All memory accesses validated<br>(stack, packet, map)"]
    VER --> C5["✓ Program size within limits<br>(1M instructions)"]
    VER --> C6["✓ Helper function args validated<br>(correct types and ranges)"]
    VER --> C7["✓ No uninitialized memory reads<br>(stack initialization)"]
    C1 --> JIT
    C2 --> JIT
    C3 --> JIT
    C4 --> JIT
    C5 --> JIT
    C6 --> JIT
    C7 --> JIT
    JIT["JIT Compile to Native Code"] --> RUN["Execute in Kernel"]

BPF Limitations

  • No unbounded loops: The verifier ensures all loops terminate (use bpf_loop() helper for bounded iteration).
  • Stack limit: 512 bytes per BPF program stack.
  • Instruction limit: 1 million instructions per program (since 5.2).
  • No sleep: BPF programs cannot sleep or block (except in specific contexts like bpf_timer).
  • Limited recursion: No direct recursion; use bpf_tail_call() for chaining programs.
  • No dynamic memory allocation: Use maps for data storage.

BPF in the Kernel: Tracing Hooks

Tracepoints

# List available tracepoints
ls /sys/kernel/debug/tracing/events/
# block/  ext4/  kmem/  net/  oom/  sched/  syscalls/  ...

# List tracepoint format
cat /sys/kernel/debug/tracing/events/sched/sched_switch/format
# name: sched_switch
# ID: 281
# format:
#   field:unsigned short common_type;       offset:0;  size:2;
#   field:unsigned char common_flags;       offset:2;  size:1;
#   field:int common_pid;                  offset:4;  size:4;
#   field:char prev_comm[16];              offset:8;  size:16;
#   field:pid_t prev_pid;                  offset:24; size:4;
#   field:int prev_prio;                   offset:28; size:4;
#   field:long prev_state;                 offset:32; size:8;
#   field:char next_comm[16];              offset:40; size:16;
#   field:pid_t next_pid;                  offset:56; size:4;

# Use with bpftrace
bpftrace -e 'tracepoint:sched:sched_switch {
    printf("%s -> %s\n", args->prev_comm, args->next_comm);
}'

XDP (eXpress Data Path)

XDP programs run at the network driver level, before the kernel networking stack:

// xdp_drop.c — Drop packets from specific IP
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

SEC("xdp")
int xdp_drop_prog(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end)
        return XDP_PASS;

    // ... parse IP header, check source IP ...
    // If matching: return XDP_DROP;
    return XDP_PASS;
}
char _license[] SEC("license") = "GPL";

LSM (Linux Security Module) Programs

// lsm_example.c — Deny specific syscalls for a process
SEC("lsm/bprm_check_security")
int BPF_PROG(deny_exec, struct linux_binprm *bprm) {
    char comm[16];
    bpf_get_current_comm(&comm, sizeof(comm));

    // Block execution of "malware" (example)
    if (comm[0] == 'm' && comm[1] == 'a' && comm[2] == 'l')
        return -EPERM;

    return 0;
}

Real-World Observability Recipes

Recipe 1: Finding the Source of Disk I/O

# Step 1: Identify high I/O process
iotop -aoP

# Step 2: Trace I/O with file details
bpftrace -e '
tracepoint:block:block_rq_issue /pid == 1234/ {
    printf("%-16s %-6d %s %s %d bytes sector %d\n",
        comm, pid, args->rwbs,
        str(((struct block_device *)args->bd)->bd_disk->disk_name),
        args->bytes, args->sector);
}'

# Step 3: Correlate with VFS calls
bpftrace -e '
kprobe:vfs_read /pid == 1234/ {
    printf("vfs_read: %s bytes=%d\n",
        str(((struct file *)arg0)->f_path.dentry->d_name.name), arg2);
}'

Recipe 2: Network Latency Analysis

# Step 1: Measure TCP connect latency
bpftrace -e '
kprobe:tcp_connect { @start[pid] = nsecs; }
kretprobe:tcp_connect /@start[pid]/ {
    @connect_us = hist((nsecs - @start[pid]) / 1000);
    delete(@start[pid]);
}'

# Step 2: Measure TCP RTT
bpftrace -e '
kprobe:tcp_rcv_established {
    $sk = (struct sock *)arg0;
    @rtt_us = hist($sk->srtt_us >> 3);
}'

# Step 3: Trace retransmissions with stack
bpftrace -e '
kprobe:tcp_retransmit_skb {
    printf("RETRANS: %s[%d] %s\n", comm, pid, kstack);
}'

Recipe 3: Lock Contention Analysis

# Trace mutex contention
bpftrace -e '
kprobe:mutex_lock_slowpath {
    @contention[comm, kstack] = count();
    @wait_time[comm] = hist(arg1);
}'

# Trace spinlock contention
bpftrace -e '
kprobe:queued_spin_lock_slowpath {
    @contention[comm, kstack] = count();
}'

Recipe 4: Page Cache Effectiveness

# Measure page cache hit vs miss
bpftrace -e '
kprobe:add_to_page_cache_lru { @misses[comm] = count(); }
kprobe:mark_page_accessed { @hits[comm] = count(); }
interval:s:5 {
    printf("\nPage cache activity:\n");
    print(@hits);
    print(@misses);
    clear(@hits);
    clear(@misses);
}'

Building BPF Programs with libbpf

Minimal BPF Program

// minimal.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_write")
int trace_write(struct trace_event_raw_sys_enter *ctx) {
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    bpf_printk("write from pid %d\n", pid);
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

User-Space Loader

// minimal.c
#include <stdio.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include "minimal.skel.h"

int main() {
    struct minimal_bpf *skel = minimal_bpf__open_and_load();
    minimal_bpf__attach(skel);

    printf("Tracing write() calls... Ctrl-C to end.\n");
    while (1) {
        sleep(1);
    }

    minimal_bpf__destroy(skel);
    return 0;
}

Build with libbpf-tools

# Build with clang
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \
    -I/usr/include/x86_64-linux-gnu \
    -c minimal.bpf.c -o minimal.bpf.o

# Generate skeleton
bpftool gen skeleton minimal.bpf.o > minimal.skel.h

# Compile user-space
gcc -o minimal minimal.c -lbpf -lelf -lz

References

Further Reading