bpfsnoop
是一款 bpf 时代的现代化内核函数、内核跟踪点和 bpf 程序的动态追踪工具。
bpfsnoop
重命名自 btrace
,为了避免与其它 btrace
项目重名,使用 bpfsnoop
作为新名称;并上线专属网站:bpfsnoop.com。
bpfsnoop
发布 v0.3.0 版本,主要更新如下:
- 支持动态追踪多达 12 个参数的内核函数。
- 新增
--output-arg
来输出函数参数的属性,类似于 --filter-arg
。
- 新增
--output-flamegraph
来输出火焰图的折叠后的数据。
- 新增
-t
来动态追踪内核跟踪点。
- 许多改进。
- 2 次重构。
支持动态追踪多达 12 个参数的内核函数
Menglong Dong 大佬在 commit bpf, x86: allow function arguments up to 12 for TRACING 中,将 x86 架构上 fentry
/fexit
目标内核函数的参数个数限制从 6 个增加到了 12 个。
因此,bpfsnoop
会检测内核是否支持动态追踪超过 6 个参数的内核函数。
效果如下:
1
2
3
|
$ sudo ./bpfsnoop -k tcp_select_initial_window --show-func-proto
Kernel functions: (total 1)
void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, __u32 init_rcv_wnd);
|
tcp_select_initial_window
函数的参数个数为 8 个。
1
2
3
|
$ sudo ./bpfsnoop -k tcp_select_initial_window
2025/04/06 12:33:54 bpfsnoop is running..
tcp_select_initial_window args=((const struct sock *)sk=0xffff991e5dd14600, (int)__space=33280, (__u32)mss=0x5a8/1448, (__u32 *)rcv_wnd=0xffff991e45cc98dc(0x7c70/31856), (__u32 *)window_clamp=0xffff991e45cc98e4(0x7fff80/8388480), (int)wscale_ok=1, (__u8 *)rcv_wscale=0xffffa64a8060086f(0x7/7), (__u32)init_rcv_wnd=0x0/0) retval=(void) cpu=6 process=(0:swapper/6)
|
新增 --output-arg
来输出函数参数的属性,类似于 --filter-arg
参考 --filter-arg
的实现方式,新增 --output-arg
来输出函数参数的属性。
访问参数属性的实现:bice Access;实现过程请参考 btrace v0.2.0 的 “使用简单的 C 表达式过来函数参数的属性”。
效果如下:
1
2
3
4
5
|
$ sudo ./bpfsnoop -k tcp_select_initial_window --output-arg 'sk->__sk_common.skc_daddr' --output-arg 'sk->__sk_common.
skc_rcv_saddr' --output-arg 'sk->__sk_common.skc_dport' --output-arg 'sk->__sk_common.skc_num'
2025/04/06 12:42:03 bpfsnoop is running..
tcp_select_initial_window args=((const struct sock *)sk=0xffff991e64d69bc0, (int)__space=33280, (__u32)mss=0x5b4/1460, (__u32 *)rcv_wnd=0xffff991e64d6a260(0x7d78/32120), (__u32 *)window_clamp=0xffff991e64d6a244(0x7fff80/8388480), (int)wscale_ok=1, (__u8 *)rcv_wscale=0xffffa64a80de7c2f(0x7/7), (__u32)init_rcv_wnd=0x0/0) retval=(void) cpu=1 process=(735963:curl)
Arg attrs: (__be32)skc_daddr=0x1010101/16843009, (__be32)skc_rcv_saddr=0x85f1a8c0/2247207104, (__be16)skc_dport=0xbb01/47873, (__u16)skc_num=0xa5fe/42494
|
--output-arg
最多输出 4 个非字符串类型的参数属性和 1 个字符串类型的参数属性。
新增 --output-flamegraph
来输出火焰图的折叠后的数据
--output-flamegraph
是个隐藏的选项,因为它只是输出了火焰图的折叠后的数据,并没有输出火焰图的 SVG 文件。
用法如下:
1
2
3
4
|
$ sudo ./bpfsnoop -k 'ip*_rcv' --output-stack --output-flamegraph output.fold --limit-events 1000 >/dev/null
2025/04/06 12:47:31 bpfsnoop is running..
2025/04/06 12:47:38 bpfsnoop is exiting..
$ /path/to/FlameGraph/flamegraph.pl output.fold > output.svg
|

新增 -t
来动态追踪内核跟踪点
参考 eBPF Talk: tp_btf 经验分享,bpfsnoop
想要动态追踪内核跟踪点时,就需要:
- 列出所有的内核跟踪点,包括内核模块的跟踪点。
- 根据内核跟踪点的名称、参数类型和参数名称来过滤需要动态追踪的内核跟踪点。
- 使用
tp_btf
来动态追踪内核跟踪点。
--output-XXX
选项的实现完成复用 -k
选项的实现。
效果如下:
1
2
3
4
5
|
$ sudo ./bpfsnoop -t 'netif_receive_skb' --filter-pkt 'host 1.1.1.1' --output-pkt --output-arg 'skb->dev->ifindex' --output-arg 'skb->dev->name'
2025/04/06 12:55:15 bpfsnoop is running..
netif_receive_skb[tp] args=((struct sk_buff *)skb=0xffff991e4b8d4000) cpu=6 process=(0:swapper/6)
Pkt tuple: 1.1.1.1:53 -> 192.168.241.133:53768 (UDP)
Arg attrs: (int)ifindex=2, (array(char[16]))name="ens33"
|
重要改进:支持 v5.15 内核
由于 bpf_get_func_{arg,ret}
helper 的使用,bpfsnoop
只能在 v5.17 及以上内核上运行。
bpfsnoop
v0.3.0 支持 v5.15 内核,主要是将 bpf_get_func_{arg,ret}
helper 的使用改为 bpf_probe_read_kernel()
;因为 fentry
/fexit
/tp_btf
的目标内核函数/跟踪点的函数参数和返回值是放在一段连续的内存里,并由 ctx
指向该段内存,所以可以使用 bpf_probe_read_kernel()
来一次性读取全部函数参数和返回值。
因此,理论上,bpfsnoop
可以在 v5.8 及以上内核上运行,依赖于 bpf link 机制和 ringbuf。
效果如下:
1
2
3
4
5
6
7
|
$ uname -r
5.15.0-126-generic
$ sudo ./bpfsnoop -k tcp_conn_request --output-arg 'sk->__sk_common.skc_daddr' --output-arg 'sk->__sk_common.skc_rcv_saddr' --output-arg 'sk->__sk_common.skc_dport' --output-arg 'sk->__sk_common.skc_num'
2025/04/06 13:03:27 bpfsnoop is running..
tcp_conn_request args=((struct request_sock_ops *)rsk_ops=0xffffffff99849b00, (const struct tcp_request_sock_ops *)af_ops=0xffffffff98944420, (struct sock *)sk=0xffff900816392bc0, (struct sk_buff *)skb=0xffff90080316f100) retval=(int)0 cpu=3 process=(0:swapper/3)
Arg attrs: (__be32)skc_daddr=0x0/0, (__be32)skc_rcv_saddr=0x0/0, (__be16)skc_dport=0x0/0, (__u16)skc_num=0x16/22
|
总结
bpfsnoop
是一款 bpf 时代的现代化内核函数、内核跟踪点和 bpf 程序的动态追踪工具:
- 支持输出 LBR 记录。
- 支持反汇编内核函数和 bpf prog。
- 支持输出函数调用栈。
- 支持输出带类型信息的参数和带类型的返回值。
- 支持使用简单的 C 表达式来过滤函数参数的属性。
- 支持根据函数参数来过滤需要动态追踪的内核函数列表。
- 支持使用 pcap-filter(7) 语法来过滤网络包。
- 支持
--output-pkt
输出网络包里的五元组信息。
- 支持动态追踪多达 12 个参数的内核函数。
- 支持
--output-arg
来输出函数参数的属性,类似于 --filter-arg
。
- 支持
--output-flamegraph
来输出火焰图的折叠后的数据。
- 支持
-t
来动态追踪内核跟踪点。
未来将支持更多高级功能,敬请期待!
bpfsnoop
项目地址:bpfsnoop。