bpftrace探测点完全手册:kprobe、uprobe、tracepoint与fentry使用详解
【免费下载链接】bpftraceHigh-level tracing language for Linux项目地址: https://gitcode.com/gh_mirrors/bp/bpftrace
bpftrace 是 Linux 上的高级追踪语言,而**探测点(Probe)**是 bpftrace 脚本的核心——它决定了追踪"发生在哪里"。这篇文章带你完整掌握 bpftrace 最常用的四类探测点:kprobe(内核函数)、uprobe(用户态函数)、tracepoint(静态追踪点)和fentry(基于 BTF 的近零开销函数追踪),覆盖"如何列出可用探测点"、"四类探测点怎么选"等高频问题,新手和普通用户也能直接上手。
上图展示了 bpftrace 各类探测点与 Linux 内核分层的对应关系:kprobe 一族向内核对内核代码做动态插桩,uprobe 一族面向用户态应用,tracepoint 是编译进内核的静态追踪点,而 fentry 则依附于 BTF 类型信息。
🧭 快速上手:如何列出系统支持的探测点
写脚本之前,最实用的一招是先用bpftrace -l列出探测点,看看当前系统到底支持什么:
bpftrace -l 'kprobe:*file*' # 列出所有带 "file" 字样的 kprobe bpftrace -l 't:syscalls:*openat*' # 用短名 t 列出 openat 相关 tracepoint bpftrace -lv 'fentry:tcp_reset' # -v 附加参数类型信息加上-v参数后,bpftrace 会进一步显示每个探测点的参数名和参数类型(如fentry:tcp_reset的sk、skb两个结构体指针),帮你省掉查源码的时间。探测点还支持通配符,甚至可以"多探测点共享一个动作":
kprobe:tcp_reset,kprobe:*socket* { printf("Entered: %s\n", probe); }详细的列出探测点方法可参考 man/adoc/bpftrace.adoc。
🧱 四类核心探测点详解
1️⃣ kprobe / kretprobe:内核函数动态追踪
kprobe 是 bpftrace 最经典的探测点,实现对任意内核函数的动态插桩——每当该函数被调用,BPF 程序就会运行一次:
kprobe:tcp_reset { @tcp_resets = count(); }- 变体:
kprobe[:模块]:函数,例如kprobe:kvm:x86_emulate_insn可追踪 KVM 内核模块中的函数; - 短名:
k(kprobe)、kr(kretprobe); - 参数访问:通过
argN按寄存器顺序取参(x86_64 上前 6 个非浮点参数走寄存器),需要自己写类型转换来解引用结构体; - kretprobe:在函数返回时触发,只能拿到返回值
retval,拿不到入参。常见套路是"入口存进 map、出口取出来"; - 还支持按
文件:行号定位(需 DWARF 调试信息),方便在函数体内部打断点式追踪。
完整细节见 docs/language.md。
2️⃣ uprobe / uretprobe:用户态函数与库追踪
uprobe 是 kprobe 在用户态的镜像版本,可以追踪任意进程里的函数调用:
uprobe:/bin/bash:readline { printf("arg0: %d\n", arg0); }- 短名:
u(uprobe)、ur(uretprobe); - 追踪库时写库名即可:
uprobe:libc:malloc会自动通过/etc/ld.so.cache解析出完整路径;同一库存在多个版本时(如libssl.so.3),建议显式写带版本号的 SONAME 避免挂错; - C++ 程序有惊喜:加
cpp限定符后可直接用人类可读的函数名(含MyApp::Server::*通配符),不必去记编译器混淆后的名字; - uretprobe拿
retval,但不能拿入参(与 kretprobe 同样的限制); - ⚠️ 追踪 C++ 时
arg0可能是this指针;追踪 Golang 等自带运行时的语言时,uretprobe 的返回地址改写机制可能引发 runtime 报错,需要留意。
完整细节见 docs/language.md。
3️⃣ tracepoint:最稳定的静态追踪点
tracepoint 是内核源码中预先定义、编译进内核的静态追踪点。与 kprobe 不同,tracepoint 不依赖某个内核函数恰好存在,接口更加稳定,升级内核后脚本通常不用改:
tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); }- 短名:
t; - 参数通过结构化的
args对象按名字访问(args.filename),无需手动类型转换; - 官方工具大量基于它实现,例如仓库自带的 tools/opensnoop.bt 就同时挂在了
sys_enter_open/openat/openat2三个 tracepoint 上,逐条打印谁打开了哪个文件; - 追求极致性能时还有
rawtracepoint(短名rt),同样的挂点、更低的开销。
完整细节见 docs/language.md。
4️⃣ fentry / fexit:BTF 加持的近零开销内核追踪
fentry/fexit 是"内核函数追踪"的新一代方案(早期叫 kfunc/kretfunc,旧名仍可用),与 kprobe 目标相同,但优势明显:
- 基于eBPF trampoline机制,调用开销接近于零;
- 利用BTF 类型信息在编译期自动推导参数类型,不需要手动类型转换,且对内核签名的小改动更稳健;
- 参数通过
args对象按名字访问:args.fd、args.skb; - fexit 可以拿到入参,这是它相比 kretprobe 的一大优势;
- 短名:
f(fentry)、fr(fexit);要求内核提供 BTF(可用bpftrace --info确认); - 还有
fentry:bpf:...变体,可以直接挂在正在运行的其他 BPF 程序上,用于调试 BPF 程序本身。
fexit:fget { printf("fd %d name %s\n", args.fd, str(retval.f_path.dentry.d_name.name)); }完整细节见 docs/language.md。
⚖️ 选型指南:kprobe、uprobe、tracepoint、fentry 怎么选
| 探测点 | 追踪目标 | 参数访问 | 稳定性 / 开销 | 适用场景 |
|---|---|---|---|---|
| kprobe | 任意内核函数 | argN+ 手动转换 | 动态插桩,开销中等 | 没有 BTF、或需要按偏移量挂函数中间 |
| fentry | 任意内核函数 | args(自动类型) | 需 BTF,开销近零 | 新内核上追踪内核函数的首选 |
| tracepoint | 内核标准事件 | args(自动类型) | 最稳定,开销低 | 系统调用、网络、调度器等标准化事件 |
| uprobe | 用户态函数 / 库 | argN(DWARF 时为args) | 动态插桩 | 应用层调试:malloc、readline 等 |
一句话决策:追标准事件选 tracepoint;追内核函数选 fentry(有 BTF 时)否则 kprobe;追用户程序选 uprobe(应用自带 USDT 探针时则用usdt,短名U)。
🔧 实战:追踪"谁打开了文件"
把前面的知识串起来——追踪 open 系统调用时,入口和出口要配对:入口先记下文件名,出口再按线程把文件名和返回码(文件描述符或负 errno)关联起来。官方工具 tools/opensnoop.bt 就是这个模式的完整实现,运行后可以看到:
PID COMM FD ERR PATH 37421 bpftrace 33 0 /sys/kernel/debug/tracing/events/syscalls/...类似的现成工具(execsnoop、tcplife、runqlat 等)都在 tools/ 目录下,是学习各种探测点用法的最佳范本;所有探测点的速查表(含 profile、hardware、interval 等 15 种)见 docs/language.md。
📚 小结
- 用
bpftrace -l/bpftrace -lv先列出探测点,再动手写脚本,可以少走 90% 的弯路; - kprobe / uprobe 灵活但参数靠
argN硬取,适合"哪里都能插"的动态场景; - tracepoint / fentry 基于静态定义与 BTF 类型,参数按名访问,脚本更稳、更易读,是官方推荐方向;
- 记住短名
k、kr、u、ur、t、f、fr,能让脚本更短更好维护。
掌握这四种探测点,你就拿到了 bpftrace 内核追踪与用户态调试的钥匙,下一步不妨直接读一读 tools/opensnoop.bt 的源码,感受真实工具中 tracepoint 的写法。
【免费下载链接】bpftraceHigh-level tracing language for Linux项目地址: https://gitcode.com/gh_mirrors/bp/bpftrace
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考