面临的问题
内容摘录自 :
BTFGen: 让 eBPF 程序可移植发布更近一步 - 深入浅出eBPF
BTFGen: One Step Closer to Truly Portable eBPF Programs
第2个链接是原文,第1个链接为翻译。
eBPF 程序需要访问内核结构来获取需要的数据,因此依赖于内核结构的布局。为特定内核版本编译的 eBPF 程序通常不能在另一个内核版本上工作,这是因为相关的内核数据结构布局可能会发生了变化:比如字段添加、删除,或类型被改变,甚至内核编译配置的改变也会改变整个结构布局。例如,禁用 CONFIG_THREAD_INFO_IN_TASK
会改变 task_struct 的所有成员变量的偏移摘录[1]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
unsigned int __state;
#ifdef CONFIG_PREEMPT_RT
/* saved state for "spinlock sleepers" */
unsigned int saved_state;
#endif
...
|
该问题的解决通常是在目标机器使用内核头文件编译 eBPF 程序并进行加载,BCC 项目所使用的正是这种方式。但该方法存在以下问题:
- 必须在目标机器上安装占用大量空间的编译器;
- 编译程序时需要资源,在某些情况下可能会影响工作负载的调度;
- 编译需要相当长的时间,因此事件采集会存在一些延迟;
- 依赖于目标机器上安装内核头文件包。
解决
libbpf + CO-RE 正是解决上述问题而提出的方案,即一次编译即可到处运行,BPF CO-RE (Compile Once – Run Everywhere)[3]介绍了此方案的实现细节。简单的来说,就是在加载BPF程序时通过BTF(包含内核信息的文件类型格式)来知晓内核结构的相关信息。但是,BTF需要在目标内核编译时配置CONFIG_DEBUG_INFO_BTF=y
才能提供,因而一旦目标系统的内核没有开启此选项,那么加载BPF程序将找不到BTF,也就无法实现CO-RE。
一种解决方案是使用事先生成好的目标内核的BTF文件,连同BPF程序一起发布,在BPF程序加载时指定自带的BTF文件路径。而这种方案的所面临的的问题在于,内核版本众多,需要为所有目标内核制作其BTF文件;二是制作出来的BTF文件大小虽说只有几M,但是当目标内核很多时,累加后的BTF集大小也很大。为此,有以下解决方案:
-
btfhub 一个已经为众多内核生成其对应的BTF文件,比如centos7,8, ubuntu等,详情见仓库[4].
-
btfgen 为BPF程序生成最小化的BTF文件,只保留BTF文件中bpf程序使用的那部分,详情见仓库[5].
图片来源: BTFGen: One Step Closer to Truly Portable eBPF Programs
btfgen和bpftool都可以用于生成最小化的BTF文件。其实本质上两者是一个项目,现在btfgen已经合入bpftool,作为其一个子功能,原项目也不再维护。
btfgen
编译安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
git clone https://github.com/kinvolk/btfgen.git --recursive
cd btfgen
make
./btfgen --help
Usage: btfgen [OPTION...]
-i, --input=input dir with source BTF files to use
--object=object path of object file to generate BTFs for
-o, --output=output dir to output the result BTF files
-v, --verbose display libbpf debug messages
-?, --help Give this help list
--usage Give a short usage message
Mandatory or optional arguments to long options are also mandatory or optional
|
使用之:
1
2
3
|
./btfgen --inputdir=/tmp/demo/input \
--outputdir=/tmp/demo/min_btf_out \
--object=/home/anolis/dev/tcpstates/tcpstates.bpf.o
|
btfhub里包含了一个使用bpftool生成最小化btf的脚本.
见: https://github.com/kinvolk/inspektor-gadget/blob/v0.4.2/tools/btfgen.sh
目前btfgen已经合并到bpftool,可以使用bpftool来生成最小化BTF文件.
1
2
3
4
5
|
git clone --recurse-submodules https://github.com/libbpf/bpftool.git
git submodule update --init
cd src
make
make install
|
使用之:
1
2
3
|
bpftool gen min_core_btf /tmp/demo/input/vmlinux.btf \
/tmp/demo/min_btf_out/vmlinux.btf \
/home/anolis/dev/tcpstates/tcpstates.bpf.o
|
在代码中使用生成后的最小化BTF文件:
1
2
|
LIBBPF_OPTS(bpf_object_open_opts, open_opts,
.btf_custom_path = "/tmp/demo/min_btf_out/vmlinux.btf");
|
重新编译运行:
1
2
3
4
5
6
7
8
|
[root@localhost build]# ./tcpstates
SKADDR PID COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff96d21a693600 1099095 node 192.168.107.181 0 20.50.73.10 443 CLOSE -> SYN_SENT 0.000
ffff96d21a693600 0 swapper/3 192.168.107.181 40404 20.50.73.10 443 SYN_SENT -> ESTABLISHED 229.687
ffff96d21a693600 0 swapper/3 192.168.107.181 40404 20.50.73.10 443 ESTABLISHED -> CLOSE_WAIT 951.627
ffff96d21a693600 1099095 node 192.168.107.181 40404 20.50.73.10 443 CLOSE_WAIT -> LAST_ACK 1.482
ffff96d21a693600 1099095 node 192.168.107.181 40404 20.50.73.10 443 LAST_ACK -> LAST_ACK 0.165
ffff96d21a693600 1099038 node 192.168.107.181 40404 20.50.73.10 443 LAST_ACK -> CLOSE 0.067
|
参考链接
-
BTFGen: 让 eBPF 程序可移植发布更近一步 - 深入浅出eBPF
-
kinvolk/btfgen
-
BPF CO-RE (Compile Once – Run Everywhere)
-
GitHub - aquasecurity/btfhub: BTFhub, in collaboration with the BTFhub Archive repository, supplies BTF files for all published kernels that lack native support for embedded BTF. This joint effort ensures that even kernels without built-in BTF support can effectively leverage the benefits of eBPF programs, promoting compatibility across various kernel versions.
-
kinvolk/btfgen