eBPF:环境预检与内核配置
介绍 eBPF 开发前需要开启的内核配置项,以及一个自动检查内核配置、kprobe 符号、tracepoint 和 vmlinux 的预检脚本。
运行 eBPF 程序前,内核需要开启一系列配置项,并且目标符号(kprobe)和事件(tracepoint)必须真实存在。如果环境不满足,程序加载时会报各种难懂的错误。
本文先给出需要开启的内核配置清单,再介绍一个自动化预检脚本,最后说明如何用脚本自动维护探针列表。
一、内核需要开启的配置
1.1 eBPF 基础特性
来源:BCC 官方文档(docs/kernel_config.md)。
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_IKHEADERS=y
说明:
CONFIG_BPF_SYSCALL:提供bpf()系统调用。CONFIG_BPF_JIT:BPF 程序 JIT 编译。CONFIG_BPF_EVENTS:支持 kprobe/tracepoint 向 BPF 传递事件。CONFIG_IKHEADERS:内核头文件打包在/sys/kernel/kheaders.tar.xz,部分工具需要。
如果要用 BTF(BPF Type Format),需要额外开启:
CONFIG_DEBUG_INFO_BTF=y
1.2 探针与跟踪支持
# tracepoint
CONFIG_TRACEPOINTS=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
# kprobe
CONFIG_KPROBES=y
CONFIG_KPROBE_EVENTS=y
# uprobe
CONFIG_UPROBES=y
CONFIG_UPROBE_EVENTS=y
修改方式:编辑 .config 后重新 make,或使用 scripts/config 命令。
二、预检脚本
以下脚本可以在 eBPF 开发前一次性检查环境,输出彩色 PASS/FAIL 结果,并把缺失详情写入日志文件。
2.1 检查内核配置
读取 /boot/config-$(uname -r)(或 /proc/config.gz),检查关键配置是否为 =y:
kernel_config_path=/boot/config-$(uname -r)
function kernel_config() {
config_field="$1=y"
ret=0
if [ -f $kernel_config_path ]; then
ret=$(grep ${config_field} ${kernel_config_path} | wc -l)
elif [ -f "/proc/config.gz" ]; then
ret=$(zcat /proc/config.gz | grep ${config_field} | wc -l)
fi
echo $ret
}
# 检查 eBPF 相关配置
for cfg in CONFIG_BPF CONFIG_BPF_SYSCALL CONFIG_BPF_JIT CONFIG_BPF_EVENTS; do
echo "$cfg = $(kernel_config $cfg)"
done
# 检查探针相关配置
for cfg in CONFIG_TRACEPOINTS CONFIG_HAVE_SYSCALL_TRACEPOINTS \
CONFIG_KPROBES CONFIG_KPROBE_EVENTS \
CONFIG_UPROBES CONFIG_UPROBE_EVENTS; do
echo "$cfg = $(kernel_config $cfg)"
done
2.2 检查 kprobe 符号是否存在
/proc/kallsyms 中列出了可探测的内核符号。预检脚本把符号表读入字典,再逐个确认需要的函数:
kprobe_path=/proc/kallsyms
declare -A kprobe_func_dict
for func in `cat $kprobe_path | awk -F ' ' '{print $3}'`; do
kprobe_func_dict[$func]=1
done
kprobe_func=("finish_task_switch" "blk_account_io_start" "sys_write"
"tcp_v4_connect" "tcp_sendmsg" "udp_sendmsg" ...)
found=0
not_found=0
for kprobe in ${kprobe_func[*]}; do
if [ -z ${kprobe_func_dict[$kprobe]} ]; then
echo "not found kprobe func symbol $kprobe" >> precheck.log
echo "find similar symbol:" >> precheck.log
echo `grep $kprobe $kprobe_path` >> precheck.log
((not_found++))
else
((found++))
fi
done
如果符号缺失,日志会同时给出相似符号,方便确认是否是命名差异。
2.3 检查 tracepoint 事件
tracepoint 的可用列表位于:
trace_path=/sys/kernel/debug/tracing/available_events
格式为 subsystem:event,例如 sched:sched_switch、syscalls:sys_enter_write。预检脚本读取该文件后逐一比对:
trace_func=("sched:sched_switch" "syscalls:sys_enter_write"
"syscalls:sys_exit_write" "sock:inet_sock_set_state" ...)
2.4 查找 vmlinux
BTF 或符号调试需要 vmlinux,脚本按常见路径依次查找:
os=$(uname -r)
paths=(
"/sys/kernel/btf/vmlinux"
"/lib/modules/$os/vmlinux-$os"
"/lib/modules/$os/build/vmlinux"
"/usr/lib/modules/$os/kernel/vmlinux"
"/usr/lib/debug/boot/vmlinux-$os.debug"
"/usr/lib/debug/lib/modules/$os/vmlinux"
)
2.5 完整输出
综合检查后输出示例:
OS Release: ...
Kernel Version: ...
bpf feature: bpf_syscall[on] bpf_jit[on] bpf_events[on] bpf_vm[on]
kernel event: tracepoint[on] syscall_tracepoint[on] uprobe[on] kprobe[on]
vmlinux found: /sys/kernel/btf/vmlinux
Check Kprobe [PASS]
Check TracePoint [PASS]
三、自动化维护探针列表
预检脚本中需要探测的 kprobe/tracepoint 列表,可以通过解析 Go 源码中的注册代码自动生成,避免手工维护:
kprobe_regx='Kprobe\(\"(.*?)\"'
trace_regx='Tracepoint\((\".*?\",\s+\".*?\")'
for file in `ls ../ | grep ^module_`; do
for func in `grep -Eo $kprobe_regx $file`; do
kprobe_func+=${func#Kprobe\(}
kprobe_func+=$" "
done
for func in `grep -Eo $trace_regx $file`; do
func=${func#Tracepoint\(}
trace_point_func+=$(echo $func | sed -E 's/",\s+"/':'/g')
trace_point_func+=$" "
done
done
再通过 sed 在预检脚本的标记区间内自动替换:
#<<kprobe-begin>>
kprobe_func=(...)
#<<kprobe-end>>
#<<tracepoint-begin>>
trace_func=(...)
#<<tracepoint-end>>
kprobe_begin_line=$(awk '/#<<kprobe-begin>>/{print NR}' precheck.sh)
kprobe_end_line=$(awk '/#<<kprobe-end>>/{print NR}' precheck.sh)
# 删除旧内容
sed -i "$((kprobe_begin_line+1)), $((kprobe_end_line-1))d" precheck.sh
# 写入新内容
sed -i "$((kprobe_begin_line+1))i kprobe_func=(${kprobe_func[*]})" precheck.sh
这样源码新增探针后,只需重新运行生成脚本,预检列表即可同步。
四、总结
- eBPF 依赖内核配置:
CONFIG_BPF、BPF_SYSCALL、BPF_JIT、BPF_EVENTS、IKHEADERS,BTF 需要DEBUG_INFO_BTF。 - kprobe 符号需要在
/proc/kallsyms中存在,tracepoint 需要在available_events中存在。 - 预检脚本通过 PASS/FAIL 输出和详细日志,快速定位环境问题。
- 探针列表可以从源码注册点自动生成,配合 sed 标记区间实现零手工维护。
相关脚本
整理自 cyy-tag/bash-example 仓库: