4

gobpf 使用示例:开发环境及 Hello World

 3 years ago
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.
neoserver,ios ssh client

前言

gobpf 使用示例系列记录使用 gobpf 编写 eBPF 程序的一些例子。

搭建开发环境

所有示例程序都基于 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK