搭建开发环境¶
所有示例程序都基于 Ubuntu 20.04 和 Go 1.6 进行编写,同时所有示例使用的 github.com/iovisor/gobpf 版本为 v0.1.1
- 可以通过各种方式安装 Ubuntu 20.04
- 同样可以通过各种方式安装 Go 1.6
安装完 Ubuntu 和 Go 后,还需要安装编译 eBPF 程序所需的编译工具和内核源码:
$ sudo apt update
$ sudo apt install build-essential git make libelf-dev libelf1 \
clang llvm strace tar make bpfcc-tools linux-headers-$(uname -r) gcc-multilib
$ cd /tmp/ && \
git clone --depth 1 git://kernel.ubuntu.com/ubuntu/ubuntu-focal.git && \
sudo mv ubuntu-focal /kernel-src && \
cd /kernel-src/tools/lib/bpf && \
sudo make && sudo make install prefix=/usr/local && \
sudo mv /usr/local/lib64/libbpf.* /lib/x86_64-linux-gnu/
第一个 eBPF 程序¶
第一个 eBPF 程序将 trace 所有的 open 系统调用,显示 open 系统调用调用时的文件路径参数
eBPF C 代码如下(hello.c):
#include <linux/bpf.h>
#include <linux/ptrace.h>
#include <linux/version.h>
#include <bpf_helpers.h>
SEC("kprobe/do_sys_open")
int kprobe__do_sys_open(struct pt_regs *ctx)
{
char file_name[256];
bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));
char fmt[] = "open file %s\n";
bpf_trace_printk(fmt, sizeof(fmt), &file_name);
return 0;
}
char _license[] SEC("license") = "GPL";
通过下面的方法编译出最终的 hello.o 文件:
$ clang -O2 -emit-llvm -I/kernel-src/tools/testing/selftests/bpf -c hello.c -o hello.ll
hello.c:11:48: warning: incompatible integer to pointer conversion passing 'unsigned long' to parameter of type 'const void *' [-Wint-conversion]
bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));
^~~~~~~~~~~~~~~~~~
/kernel-src/tools/testing/selftests/bpf/bpf_helpers.h:398:26: note: expanded from macro 'PT_REGS_PARM2'
#define PT_REGS_PARM2(x) ((x)->rsi)
^~~~~~~~~~
1 warning generated.
$ llc -march=bpf -filetype=obj -o hello.o hello.ll
$ ls hello.o
hello.o
然后在 Go 中使用 gobpf 加载的方法如下(hello.go):
package main
import (
"fmt"
"time"
"github.com/iovisor/gobpf/elf"
)
func main() {
mod := elf.NewModule("hello.o")
err := mod.Load(nil)
if err != nil {
panic(err)
}
err = mod.EnableKprobes(0)
if err != nil {
panic(err)
}
for {
fmt.Println("Waiting...")
time.Sleep(10 * time.Second)
}
}
编译运行:
$ go mod init $ go get github.com/iovisor/gobpf $ go build hello.go $ sudo ./hello Waiting...
新开一个 shell 窗口,然后在窗口内执行下面的命令可以看到被 trace 的 open 系统调用:
$ sudo cat /sys/kernel/debug/tracing/trace_pipe ... systemd-journal-364 [000] .... 16819.802559: 0: open file /proc/492/attr/current systemd-journal-364 [000] .... 16819.802573: 0: open file /proc/492/sessionid systemd-journal-364 [000] .... 16819.802583: 0: open file /proc/492/loginuid systemd-journal-364 [000] .... 16819.802594: 0: open file /proc/492/cgroup ...
备注:
多次运行 hello 程序会出现如下错误:
$ sudo ./hello panic: cannot write "p:pdo_sys_open do_sys_open\n" to kprobe_events: write /sys/kernel/debug/tracing/kprobe_events: file exists
可以通过下面的方法清理上次运行的遗留信息:
$ echo "" |sudo tee /sys/kernel/debug/tracing/kprobe_events
然后再运行 sudo ./hello 就不会有问题了。
P.S. 本文的所有代码在 Github 上都有一份完整版:https://github.com/mozillazg/gobpf-examples/tree/master/1-helloword
Comments