ePBF
ePBF
目录
编写 BPF 程序
对于大多数开发者而言,更多的是基于 BPF 技术之上编写解决我们日常遇到的各种问题,当前 BCC 和 BPFTrace 两个项目在观测和性能分析上已经有了诸多灵活且功能强大的工具箱,完全可以满足我们日常使用。
更早期的工具则是使用 C 语言来编写 BPF 程序,使用 LLVM clang 编译成 BPF 代码,这对于普通使用者上手有不少门槛当前仅限于对于 eBPF 技术更加深入的学习场景。
BCC 版本 HelloWorld
[title(BCC 整体架构)]
使用 BCC 前端绑定语言 Python 编写的 Hello World 版本:
#!/usr/bin/python3
from bcc import BPF
# This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone
prog = """
int kprobe__sys_clone(void *ctx) {
bpf_trace_printk("Hello, World!\\n");
return 0;
}
"""
b = BPF(text=prog, debug=0x04)
b.trace_print()
运行程序前需要安装过 bcc 相关工具包,当运行正常的时候我们发现每当 sys_clone
系统调用时,运行的控制台上就会打印 “Hello, World!”,在打印文字前面还包含了调用程序的进程名称,进程 ID 等信息;
如果运行报错,可能是缺少头文件,一般安装 kernel-devel 包即可。
# python ./hello.py
kubelet-8349 [006] d... 33637334.829981: : Hello, World!
kubelet-8349 [006] d... 33637334.838594: : Hello, World!
kubelet-8349 [006] d... 33637334.843788: : Hello, World!
BPFTrace
BPFTrace 是基于 BPF 和 BCC 的开源项目,与 BCC 不同的是其提供了更高层次的抽象,可以使用类似 AWK 脚本语言来编写基于 BPF 的跟踪或者性能排查工具,更加易于入门和编写,该工具的主要灵感来自于 Solaris 的 D 语言。BPFTrace 更方便与编写单行的程序。BPFTrace 与 BCC 一样也是 IO Visor 组织下的项目,仓库参见 bpftrace。更加深入的学习资料参见:Reference Guide 和 One-Liner Tutorial。
BPFTrace 使用 LLVM 将脚本编译成 BPF 二进制码,后续使用 BCC 与 Linux 内核进行交互。从功能层面上讲,BPFTrace 的定制性和灵活性不如 BCC,但是比 BCC 工具更加易于理解和使用,降低了 BPF 技术的使用门槛。
# 统计进程调用 sys_enter 的次数
#bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
Attaching 1 probe...
^C
@[bpftrace]: 6
@[systemd]: 24
@[snmp-pass]: 96
@[sshd]: 125
# 统计内核中函数堆栈的次数
# bpftrace -e 'profile:hz:99 { @[kstack] = count(); }'
Attaching 1 probe...
^C
[...]
@[
filemap_map_pages+181
__handle_mm_fault+2905
handle_mm_fault+250
__do_page_fault+599
async_page_fault+69
]: 12
[...]
@[
cpuidle_enter_state+164
do_idle+390
cpu_startup_entry+111
start_secondary+423
secondary_startup_64+165
]: 22122
C 语言原生方式
采用 LLVM Clang 的方式编译会涉及到内核编译环境搭建,而且还需要自己编译 Makefile 等操作,属于高级用户使用:
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
static int (*bpf_trace_printk)(const char *fmt, int fmt_size,
...) = (void *)BPF_FUNC_trace_printk;
SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog(void *ctx) {
char msg[] = "Hello, BPF World!";
bpf_trace_printk(msg, sizeof(msg));
return 0;
}
char _license[] SEC("license") = "GPL";
#include "bpf_load.h"
#include <stdio.h>
int main(int argc, char **argv) {
if (load_bpf_file("bpf_program.o") != 0) {
printf("The kernel didn't load the BPF program\n");
return -1;
}
read_trace_pipe();
return 0;
}
Makefile 文件(部分)
build: ${BPFCODE.c} ${BPFLOADER}
$(CLANG) -O2 -target bpf -c $(BPFCODE:=.c) $(CCINCLUDE) -o ${BPFCODE:=.o}
其中 clang 编译中的选型 -target bpf
表明我们将代码编译成 bpf 的字节码。
完整的程序参见:hello_world;更多的样例代码可以参见对应内核中 kernel-src/samples/bpf/
下的样例代码。