BCC和libbpf的转换 本文讲述如何将基于BCC的BPF应用转换为libbpf + BPF CO-RE。BPF CO-RE可以参见上一篇博文。 为什么是libbpf和BPF CO-RE? 当编译BPF代码并生成BPF skeleton后,需要在用户空间代码中包含libbpf和skeleton头文件: #include <bpf/bpf.h> #include <bpf/libbpf.h> 日志 如果程序运行不正常,最好的方式是检查libbpf的日志输出。 检测BCC与libbpf模式 在需要同时支持BCC和libbpf模式的场景下,需要检测BPF程序代码能够编译为哪种模式。 使用libbpf时必须明确指定大小。
本篇文章概述了 BPF 的主要应用,重点描述了 libbpf-tools 解决了哪些 BCC 痛点以及在 PingCAP 内部的相关实践。 Libbpf 作为 BPF 程序加载器,接管了重定向、加载、验证等功能,BPF 程序开发者只需要关注 BPF 程序的正确性和性能即可。 Libbpf-tools 在 PingCAP 的应用 在 PingCAP 内部,我们很早就开始关注 BPF 和其社区发展,以前每添加一台新机器,就得在上面安装一套 BCC 依赖,比较麻烦,自从 libbpf-tools 当前已完成 10 个 BCC/bpftrace 工具到 libbpf + BPF CO-RE 的转换 & 改进,并在内部使用。 后续会将更多的工具基于 libbpf-tools 的方式来实现。期待大家的使用反馈!
clion搭建libbpf-bootstrap开发环境 首先用clion打开libbpf-bootstrap目录,将example/c下的CMakeLists.txt导入 这个时候你会发现代码不能用clion 自带的可视化界面debug,这是因为libbpf-bootstrap目录结构太离谱了,我们只需要做一个小小的更改 将73行的 1 set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR }/libbpf/libbpf.a) 改成 1 set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/../../.. /libbpf/src/libbpf.a) 结束,享受clion的优秀
什么是libbpf-bootstrap libbpf-bootstrap是一个开源项目,旨在帮助开发者快速启动和开发使用eBPF(Extended Berkeley Packet Filter)和libbpf libbpf-bootstrap开源链接:https://github.com/libbpf/libbpf-bootstrap/ 「以下是libbpf-bootstrap的一些主要特性:」 样例程序:libbpf-bootstrap 获取源码libbpf-bootstrap // 获取libbpf-bootstrap主仓的代码 $ git clone https://github.com/libbpf/libbpf-bootstrap MKDIR .output MKDIR .output/libbpf LIB libbpf.a MKDIR /home/rice/libbpf-bootstrap /rice/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/libbpf_errno.o CC /home/rice/libbpf-bootstrap
使用 C/C++ 编写 eBPF 程序并编译为 Wasm libbpf 是一个 C/C++ 的 eBPF 用户态加载和控制库,随着内核一起分发,几乎已经成为 eBPF 用户态事实上的 API 标准,libbpf libbpf-bootstrap 为生成基于 libbpf 的 bpf 程序提供了模板,开发者可以很方便的使用该模板生成自定义的 bpf 程序。 bootstrap 是在 libbpf-bootstrap 中,根据 BCC 软件包中的libbpf-tools的类似思想创建的,但它被设计成更独立的,并且有更简单的 Makefile 以简化用户的特殊需求 我们为 wasm 程序提供了一个仅包含头文件的 libbpf API 库,您可以在 libbpf-wasm.h(wasm-include/libbpf-wasm.h)中找到它,它包含了一部分 libbpf 我们将尽最大努力使 wasm 端的 libbpf API 与通常在用户空间运行的 libbpf API尽可能相似,以便用户空间代码可以在未来直接编译为 wasm。
使用 libbpf 开发 eBPF 程序分为两部分:第一,内核态的 eBPF 程序;第二,用户态的加载、挂载、映射读取以及输出程序等。 这样,使用 libbpf 开发 eBPF 程序就可以通过以下四个步骤完成: 使用 bpftool 生成内核数据结构定义头文件。
使用 C/C++ 编写 eBPF 程序并编译为 Wasm libbpf 是一个 C/C++ 的 eBPF 用户态加载和控制库,随着内核一起分发,几乎已经成为 eBPF 用户态事实上的 API 标准,libbpf libbpf-bootstrap 为生成基于 libbpf 的 bpf 程序提供了模板,开发者可以很方便的使用该模板生成自定义的 bpf 程序。 bootstrap 是在 libbpf-bootstrap 中,根据 BCC 软件包中的libbpf-tools的类似思想创建的,但它被设计成更独立的,并且有更简单的 Makefile 以简化用户的特殊需求 我们为 wasm 程序提供了一个仅包含头文件的 libbpf API 库,您可以在 libbpf-wasm.h(wasm-include/libbpf-wasm.h)中找到它,它包含了一部分 libbpf 我们将尽最大努力使 wasm 端的 libbpf API 与通常在用户空间运行的 libbpf API尽可能相似,以便用户空间代码可以在未来直接编译为 wasm。
libbpf库在早期和内核源码结合的比较紧密,如今的libbpf库更加成熟,已经完全脱离内核源码独立发展。 大部分版本的内核获取libbpf版本的方法如下,从libbpf库目录的libbpf.map文件中提取最大的版本号信息。这里的"source"为内核源码所在目录。 $ cat . $ diff -qr $NATIVE_LIBBPF/trace_execve_libbpf130/tools/lib/bpf/ ~/libbpf-1.3.0/src/ Only in ~/libbpf 在一些古老的代码示例中,有<libbpf.h>这样使用头文件的用法,目前最新的ebpf项目实例,都会将libbpf库的libbpf.h以及同目录的头文件都放到bpf子目录下,因此推荐统一使用<bpf/libbpf.h 5.6 原生libbpf库与libbpf-bootstrap的若干区别 相比较第3代的 libbpf-bootstrap框架方案和第2代的传统libbpf库方案,使用hexdump命令的原生libbpf
编译bcc以及libbpf-tools,这里编译bcc 编译bcc的过程会自动下载编译依赖的libbpf和bpftool,因此可以不需要再单独下载编译libbpf和bpftool #git clone /libbpf-tools/ #make 编译通过后执行bcc中自带的libbpf的工具比如opensnoop会报错,这个是因为bcc.git提供的libbpf-tools工具集实现时使用了BTF相关接口 (LIBBPF_STRICT_ALL); /* Set up libbpf errors and debug info callback */ libbpf_set_print 和bpftool,因此可以不需要再单独下载编译libbpf和bpftool, 如果验证程序不是放在bcc/libbpf里一起编译,则需要再单独下载编译安装libbpf和bpftool。 =2 libbpf: looking for externs among 6 symbols... libbpf: collected 0 externs total libbpf: map 'trace_kf.rodata
这是一个基于 CO-RE(一次编译,到处运行)的 libbpf 的 eBPF 的开发教程,提供了从入门到进阶的 eBPF 开发实践指南,包括基本概念、代码实例、实际应用等内容。 但目前似乎很少有基于 libbpf 和 BPF CO-RE 出发的、通过案例和工具介绍 eBPF 开发的教程,因此我们发起了这个项目。 本项目主要基于 libbpf-boostrap 和 eunomia-bpf 两个框架完成,并使用 eunomia-bpf 帮助简化一部分 libbpf eBPF 用户态代码的编写。 参考:BCC 到libbpf 的转换指南【译】 - 深入浅出eBPF: https://www.ebpf.top/post/bcc-to-libbpf-guid/ eunomia-bpf eunomia-bpf eBPF 应用,同时内核态 eBPF 代码保证和主流的 libbpf, libbpfgo, libbpf-rs 等开发框架的 100% 兼容性。
基于libbpf实现一个跟踪kfree_skb的tracepoint类型ebpf示例: libbpf/bpftool项目地址:https://github.com/libbpf/libbpf,libbpf llvm-libs llvm-devel llvm -y 下载编译libbpf: git clone https://github.com/libbpf/libbpf.git cd libpf/src make 编译后生成libbpf.so libbpf.so.1 libbpf.so.1.3.0 libbpf.a库文件: # pwd /root/ebpf-example/libbpf/src # libbpf.a . /libbpf/src -L /root/ebpf-example/libbpf/src -l:libbpf.a -lelf -lz -o trace_kfree_skb 4.
BCC 2.2. libbpf-bootstrap 2.3 eunomia-bpf 参考资料 1. 为什么会有 eBPF 技术? 在Linux的源码包的samples/bpf/目录下,有大量Linux 提供的基于libbpf的eBPF样例代码。 2.2. libbpf-bootstrap libbpf-bootstrap是一个基于libbpf库的BPF开发脚手架,从其 github 上可以得到其源码。 同样的,libbpf-bootstrap也有非常完备的入门教程,用户可以在该处 得到详细的入门操作介绍。 eBPF 应用,同时内核态 eBPF 代码保证和主流的 libbpf, libbpfgo, libbpf-rs 等开发框架的 100% 兼容性。
BCC 2.2. libbpf-bootstrap 2.3 eunomia-bpf 2.4 快速上手开发 1. 在Linux的源码包的samples/bpf/目录下,有大量Linux 提供的基于libbpf的eBPF样例代码。 也正是因此,在本项目的开发中我们放弃了BCC, 选择了可以做到一次编译-多次运行的libbpf-bootstrap工具。 2.2. libbpf-bootstrap libbpf-bootstrap是一个基于libbpf库的BPF开发脚手架,从其 github 上可以得到其源码。 同样的,libbpf-bootstrap也有非常完备的入门教程,用户可以在该处 得到详细的入门操作介绍。
一个基础性的工具是 libbpf ,它是用 C 编写的,并在 Linux 内核源代码树的 tools/lib/bpf 目录下开发。它是处理 eBPF 的标准工具。 然而, libbpf 相当低级,因此添加了额外的工具来帮助更轻松地编写 eBPF 程序及其相应的用户空间程序。 libbpf-rs 是 libbpf 的官方 Rust 封装。然而, libbpf-rs 仍然要求使用 C 编写 eBPF 程序。 Aya 完全摆脱了对 libbpf 的依赖,采用纯粹的本地 Rust 实现。 Library Userspace eBPF Syscalls libbpf C C C bcc Python + lua C C ebpf-go ️Go C C libbpf-rs Rust C C
eunomia-bpf/eunomia-cc 文档:https://github.com/eunomia-bpf/eunomia-bpf.github.io 传统来说, eBPF 的开发方式主要有 BCC、libbpf 框架实现兼容,原先使用 libbpf 框架编写的代码几乎不需要改动即可移植;eunomia-bpf 编写的 eBPF 程序也可以使用 libbpf 框架来直接编译运行; 本地只需要下载一个很小的二进制运行时 所有的 eBPF 代码和原生的 libbpf 程序没有任何区别,使用 eunomia-bpf 开发的代码也可以在 libbpf 中无需任何改动即可编译运行。 相同的 c eBPF 代码,因此大多数 libbpf eBPF c 代码无需任何修改即可运行。 详情请参考 libbpf 对应的文档:libbpf/libbpf 我们未来会在这方面继续提升兼容性。
我们还将学习如何使用原生的 libbpf 开发用户态程序,将 eBPF 应用打包为可执行文件,实现跨内核版本分发。 libbpf 库,以及为什么需要使用它libbpf 是一个 C 语言库,伴随内核版本分发,用于辅助 eBPF 程序的加载和运行。 (enum libbpf_print_level level, const char *format, va_list args){ if (level == LIBBPF_DEBUG && ! errors and debug info callback */ libbpf_set_print(libbpf_print_fn); /* Cleaner handling of Ctrl-C return err;libbpf_set_print(libbpf_print_fn);接下来,我们打开 eBPF 脚手架(skeleton)文件,将最小持续时间参数传递给 eBPF 程序,并加载和附加
则需要在Makefile中增加对VMLINUX_BTF 的定义,值为本地编译的vmlinux的路径,如: VMLINUX_BTF := /root/linux-5.10.5/vmlinux 在BCC和libbpf > #include "path/to/your/skeleton.skel.h" 用户侧的主要代码如下: int libbpf_print_fn(enum libbpf_print_level level , const char *format, va_list args) { if (level == LIBBPF_DEBUG && ! 的日志打印 */ libbpf_set_print(libbpf_print_fn); /* BPF的BPF maps以及其他内容使用了locked类型的内存, libbpf不会自动设置该值 参考 bpf(2) bpf-helpers(7) BCC和libbpf的转换 Tips and Tricks for Writing Linux BPF Applications with libbpf
开发各类小工具的例子:https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md(跑一遍:3-4h)libbpf 的一些例子: https://github.com/libbpf/libbpf-bootstrap(选感兴趣的运行一下,并阅读一下源代码:2h)基于 libbpf 和 eunomia-bpf 的教程:
然而由于该方法仍然较难理解且入门存在一定的难度,因此现阶段的eBPF程序开发大多基于一些工具,比如: BCC BPFtrace libbpf go-libbpf etc 目前使用较多的是 BCC 工具, 但本项目放弃了 BCC ,选择了 libbpf 作为我们的开发工具。 也正是因此,在本项目的开发中我们放弃了BCC,选择了可以做到一次编译-多次运行的 libbpf 工具。 libbpf-bootstrap 是一个基于 libbpf 库的BPF开发脚手架,从其 github 上可以得到其源码。 我们选择现代 C++ 语言(cpp20)开发 Eunomia 的时候也主要是看中和 libbpf 库以及 bpf 代码的良好兼容性,libbpf 库目前还在迅速更新迭代过程中,我可以直接基于 libbpf
的一些例子:https://github.com/libbpf/libbpf-bootstrap (选感兴趣的运行一下,并阅读一下源代码,2h)基于 libbpf 和 eunomia-bpf 的教程: 在Linux的源码包的samples/bpf/目录下,有大量Linux提供的基于libbpf的eBPF样例代码。 也正是因此,在本项目的开发中我们放弃了BCC,选择了可以做到一次编译-多次运行的libbpf-bootstrap工具。 同样的,libbpf-bootstrap也有非常完备的入门教程,用户可以在该处得到详细的入门操作介绍。 eBPF 应用,同时内核态 eBPF 代码保证和主流的 libbpf, libbpfgo, libbpf-rs 等开发框架的 100% 兼容性。