gobpf 使用示例:开发环境及 Hello World
source link: https://mozillazg.com/2021/04/ebpf-gobpf-dev-env-and-hello-first-program.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
搭建开发环境¶
所有示例程序都基于 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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK