bpftrace:一行命令的 eBPF 动态追踪
更新时间:2026-09-19。bpftrace 是基于 eBPF 的高级追踪语言(类似 awk/DTrace 风格),不用写完整 C 程序就能在内核或用户态函数上挂探针,实时统计延迟分布、慢调用、事件计数。它和 bcc 工具集是同一技术底座的两种用法:bcc 提供现成工具,bpftrace 适合临时自定义。底层机制与 perf 同源但更灵活。
一、基本结构
bash
probe /filter/ { action }
探针点 过滤条件 动作(用聚合表统计)bash
# 谁在打开文件(tracepoint)
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
# 统计各进程发起的 read 系统调用次数
bpftrace -e 'tracepoint:syscalls:sys_enter_read { @[comm] = count(); }'
# 对 vfs_read 计时,输出延迟直方图(微秒)
bpftrace -e 'kprobe:vfs_read { @ts[tid] = nsecs; }
kretprobe:vfs_read /@ts[tid]/ { @lat_ns = hist(nsecs - @ts[tid]); delete(@ts[tid]); }'
# 99 线风格:只看超过 1ms 的慢 read,按进程聚合
bpftrace -e 'kretprobe:vfs_read /@ts[tid] && (nsecs-@ts[tid])>1000000/ { @slow[comm]=count(); }'二、常用探针与内置量
| 探针 | 挂载点 |
|---|---|
tracepoint:syscalls:sys_enter_xxx | 稳定的系统调用入口 |
kprobe:func / kretprobe:func | 任意内核函数进入/返回 |
uprobe:bin:func / uretprobe | 用户态函数(如 libc、自研 .so) |
profile:hz:99 | 定时采样所有 CPU(做 CPU 火焰图式剖析) |
sched:sched_switch | 调度切换,分析 off-CPU/唤醒 |
内置变量:pid/tid/comm(进程名)、cpu、nsecs(纳秒时间戳)、args->xxx(参数)、retval(返回值)、kstack/ustack(内核/用户栈)。
三、聚合:count/hist/sum/avg
bash
# 按进程统计 CPU 采样(谁在烧 CPU)
bpftrace -e 'profile:hz:99 { @[comm, kstack] = count(); }'
# 按 PID 统计块 IO 大小分布
bpftrace -e 'tracepoint:block:block_rq_issue { @bytes[pid,comm] = hist(args->bytes); }'
# TCP 重传统计(按目标端口)
bpftrace -e 'kprobe:tcp_retransmit_skb { @[args->sk->__sk_common.skc_dport] = count(); }'
# 函数平均/最大耗时
bpftrace -e 'kretprobe:my_func { @avg = avg(nsecs-@s[tid]); @max=max(...); }'hist() 自动输出 2 的幂次方桶直方图(和 Prometheus histogram 的桶思想一致),按回车结束时打印,这是看尾延迟最直观的方式。
四、典型场景
- 内核函数延迟:文件系统、块层、网络协议栈任意函数的耗时分布,比看平均更能抓尾延迟。
- 用户态自定义函数:对没有埋点的 C/C++ 服务用 uprobe 测某函数 P50/P99,无需改代码、无需重启(动态挂载)。
- off-CPU 分析:挂
sched_switch统计线程因等 IO/锁被切走的时间,补 perf(on-CPU)看不到的另一半。 - 临时回答一个具体问题:「谁在频繁 fsync」「哪个进程在往这个 IP 发包」,随写随用。
五、生产使用注意
- eBPF 事件在内核态运行,过滤条件尽量下推,避免对高频事件(如每次网络收包)无过滤挂载导致开销。
- 优先用稳定的 tracepoint,kprobe 函数名跨内核版本可能变。
- 需要匹配的内核头/BTF;老内核(<4.9/无 BTF)能力受限。
- uprobe 对被内联或 strip 的函数无效;PIE/ASLR 由工具自动处理但要确认版本。
- bpftrace 适合交互探查,固化的监控/工具建议沉淀成 bcc 脚本或 eBPF 程序。
坑
- 忘了在 kretprobe 配对 delete 时间戳表,长时间运行内存增长。
- 在超高频函数上无过滤挂载,把生产机拖慢。
- 混用纳秒/微秒/毫秒,直方图刻度看错。
- 依赖内联后的函数名做 uprobe,探针挂不上。
- 只在测试机写过,上生产没评估内核版本/BTF/权限(需要 CAP_BPF/root)。
内核丢包可结合 dropwatch,函数延迟的现成工具见 funclatency。
总结
bpftrace 用「探针/过滤/动作」一行脚本把 eBPF 挂到 tracepoint、kprobe、uprobe 或定时采样点,借助 count、hist、avg 等聚合直接输出按进程、调用栈或端口维度的计数与延迟直方图,特别适合临时统计任意内核或用户态函数的尾延迟、off-CPU 时间和高频事件;它比 perf 更灵活、比写完整 bcc 程序更轻,生产环境应优先用稳定 tracepoint、下推过滤并确认内核 BTF 与权限,长期监控再沉淀为 bcc 工具。