bpftrace 一行 eBPF 脚本排查"看不见"的内核 / 用户态问题

eBPF 允许在内核里跑沙箱化的小程序,无侵入地观察系统行为。
bpftrace 是 awk 风格的高级语言,让你不用 C / libbcc 也能写 eBPF
脚本。一行就能解决很多用 strace / perf 都麻烦的问题。

sudo apt install -y bpftrace
bpftrace --version    # 需要 >= 0.16,老版本功能少很多

# 内核 5.5+ / Debian 11+ / Ubuntu 20.04+

1. 一行解决的常见问题

谁在打开 /etc/passwd

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_openat
  /str(args->filename) == "/etc/passwd"/
  { printf("%s (%d) opened /etc/passwd\n", comm, pid); }
'

任何进程 open /etc/passwd 时立即打印进程名 + PID。

哪个进程在创建 socket

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_socket
  { printf("%s (%d) opened socket family=%d type=%d\n",
            comm, pid, args->family, args->type); }
'

哪个 exec 系列 syscall 在被频繁调用

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_execve
  { printf("%s -> %s\n", comm, str(args->filename)); }
'

实时显示所有 exec —— 找出 shell 死循环 / 短生命周期进程喷。

系统调用直方图

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_read
  { @[comm] = count(); }
  interval:s:5 { print(@); clear(@); exit(); }
'
# 5 秒内每个进程的 read 次数

哪个进程在 page fault

sudo bpftrace -e '
  software:major-faults:1
  { @[comm] = count(); }
'
# Ctrl-C 后看汇总

major-fault = 必须从磁盘加载 page,对应 swap / mmap miss 等慢操作。

2. 用户态 dynamic instrumentation (uprobe)

观察 nginx 调用某函数:

# 列 nginx 二进制的可探测函数
sudo bpftrace -l 'uprobe:/usr/sbin/nginx:*' | head

# 探测某个函数
sudo bpftrace -e '
  uprobe:/usr/sbin/nginx:ngx_http_process_request
  { @[comm] = count(); }
'

不需要重启 nginx、不需要改代码,实时统计某函数被调多少次。

3. 时延直方图

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_read
  { @start[tid] = nsecs; }

  tracepoint:syscalls:sys_exit_read
  /@start[tid]/
  { @us = hist((nsecs - @start[tid]) / 1000); delete(@start[tid]); }

  interval:s:5 { print(@us); clear(@us); }
'

输出每 5 秒一次 read syscall 的时延直方图(微秒):

@us:
[0]                    8 |@                                                   |
[1]                  256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    |
[2, 4)               384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   |
[4, 8)               128 |@@@@@@@@@@@@@@@@                                    |
[8, 16)               16 |@@                                                  |
[16, 32)               4 |                                                    |
[1K, 2K)               1 |                                                    |

立刻看出 read 大多 1-4 µs,偶尔有 1-2 ms 的慢——可能是磁盘 / 网络 IO
慢。

4. 内置变量速查

  • pid / tid:进程 / 线程 ID
  • comm:进程名(前 16 字符)
  • nsecs:当前时间戳(纳秒)
  • cpu:当前 CPU
  • args:tracepoint 参数(结构体)
  • kstack / ustack:内核 / 用户调用栈

5. 输出 + 聚合

@hist_name = hist(value)           # 2 的幂直方图
@lhist_name = lhist(value, 0, 100, 10)  # 线性直方图
@count[key] = count()              # 计数
@sum[key] = sum(value)             # 求和
@avg[key] = avg(value)             # 平均
@stats[key] = stats(value)         # min/max/avg/count/sum
@top10 = bottom(10, count())       # Top-N

Map 自动按 key 分组。

6. 实战:找出"哪个文件被读最多"

sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_openat
  { @opens[str(args->filename)] = count(); }

  interval:s:30
  { print(@opens, 10); clear(@opens); }
'
# 每 30 秒输出 Top 10 被 open 的文件

7. 现成脚本库:bpftrace tools

git clone https://github.com/iovisor/bpftrace
ls bpftrace/tools
# biolatency.bt   biosnoop.bt   execsnoop.bt   funcslower.bt
# opensnoop.bt    runqlat.bt    tcpaccept.bt   tcpconnect.bt
# ...

例:磁盘 IO 时延直方图

sudo bpftrace bpftrace/tools/biolatency.bt

execsnoop.bt 实时显示新建进程,tcpconnect.bt 显示所有新建 TCP 连接,
都是诊断生产问题的利器。

8. 跟 bcc 比

bcc 是用 C 写 eBPF + Python 包装,更强大但学习成本高。
bpftrace 是高级语言,限制更多但写起来快 10x。生产经验:

  • 简单监控 / 调试一次性脚本:bpftrace
  • 持续运行的复杂 agent / 工具:bcc

9. 性能影响

bpftrace 比 strace / perf 通常开销小 5-10x。但还是有开销:

  • tracepoint:几乎零(编译时插入的探测点)
  • kprobe / uprobe:每次触发都中断到 BPF 解释器,有一点开销
  • 高频事件(每秒百万级)+ map 写:可能加 5-15% CPU

生产环境 attach 前先估算事件频率,从低开销 tracepoint 开始。

10. CO-RE / libbpf-tools

最新的 BPF 工具用 CO-RE(Compile Once, Run Everywhere):

sudo apt install -y libbpf-tools
sudo execsnoop-bpfcc   # bcc 版(需要 kernel-devel)
sudo execsnoop          # libbpf-tools 版(任意内核 5.4+)

CO-RE 工具不需要内核头文件,便携性最好。生产首选 libbpf-tools。

踩过的坑

  • 老内核(< 5.0)功能阉割严重;如果没法升内核,回退到 strace / perf。
  • BTF(BPF Type Format)没启用的内核:很多 bpftrace 脚本直接报错。
    ls /sys/kernel/btf/vmlinux 看是否有,没有就只能用 kprobe 而非 fentry。
  • 一个 bpftrace 脚本里写 5+ 个 probe + 大 map → BPF verifier 拒绝
    ("too many instructions" / "stack overflow")。拆成多个脚本。
  • 容器内跑 bpftrace 没权限:需要 --privileged + --cap-add SYS_ADMIN
    或者直接在 host 上跑。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。