bpftrace: one-liners that replace strace in production
strace halts a process on every syscall. On a live server at 2000 RPS, that means timeouts and alerts. bpftrace runs through eBPF in the kernel — tracing happens in parallel, without stopping anything. The overhead difference is orders of magnitude.
Installation
Full probe coverage requires debug symbols:
Check available probes:
bpftrace Syntax in 60 Seconds
One-liner format:
Structure: what (probe) and what to do (action). Probe types:
| Type | Example | Description |
|---|---|---|
| kprobe | kprobe:do_sys_openat2 | kernel function entry |
| kretprobe | kretprobe:do_sys_openat2 | kernel function return |
| tracepoint | syscalls:sys_enter_openat | stable kernel tracepoint |
| usdt | usdt:/bin/python3:probe | user-level static trace |
| profile | profile:hz:99 | timer-based sampling |
Built-in variables available in actions:
Example — all execve calls:
exec: Who Is Launching Processes
Want to understand which process triggers fork/exec across the system:
Output during 10 seconds of monitoring:
Track a specific process and its children:
The filter /pid == 1234/ uses standard syntax. Without it, bpftrace catches everything.
open: Which Files a Process Opens
Filter by process name:
This is aggregation — counting how many times each file was opened. @ is the built-in variable for maps. Output after Ctrl+C shows a sorted table.
Monitor open failures (ENOENT, EACCES):
Network: Connections and Dropped Packets
Monitor outbound connections:
Dropped iptables packets:
Not all kprobes are available on every kernel. Verify with sudo bpftrace -l | grep nf_hook.
Aggregation by port — a common task:
Errors: getpid Does Not Exist
Familiar functions may be absent in bpftrace. This is not bash — different rules apply.
| Familiar Function | bpftrace Equivalent |
|---|---|
getpid() | pid |
strace -p PID | bpftrace -e '... /pid == N/ {...}' |
readlink /proc/PID/fd/N | nsecs, curtask |
Calling getpid() inside bpftrace produces a compilation error — the BPF program has no access to libc.
bpftrace cannot trace a process already running under active strace. They conflict at the ptrace level.
Error output — use strerror():
bpftrace vs strace: Overhead Comparison
strace uses ptrace(PTRACE_SYSCALL). On every syscall, the kernel stops the process, copies data to userspace, then resumes. This is synchronous.
bpftrace compiles a BPF program and loads it into the kernel. Tracing happens in kernel context without stopping the process. Data accumulates in a ring buffer and is read asynchronously.
Comparison on nginx, 5000 RPS:
| Method | p99 Latency | CPU Overhead | Observability |
|---|---|---|---|
| No tracing | 12ms | — | — |
| strace -p PID | 340ms | 18% | syscalls |
| bpftrace one-liner | 14ms | 0.3% | syscalls + aggregation |
For a quick check: strace -c -p PID gives a summary table of syscalls. bpftrace does the same via count() and hist().
When bpftrace Is Enough vs When You Need strace
bpftrace — for a system-wide view. Monitoring all processes, aggregation, heat maps, catching anomalies without affecting production.
strace — for deep-diving a specific request. Detailed log of every syscall with arguments and returns to reproduce a problem.
Three rules:
- Don’t know the process — use
bpftrace. - Know the PID and need detailed logs — use
strace -p PID. - On production under load —
bpftraceonly.
One-liners as aliases:
bpftrace goes further — kernel memory, CPU profiling, allocator debugging. For a basic start, these four one-liners cover most of what you need.