sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo rm -f /etc/apt/sources.list
将阿里云源添加到 /etc/apt/sources.list
中:
deb https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
执行如下命令更新源:
sudo apt update
sudo apt install -y openssl libssl-dev git curl build-essential cmake llvm-14 llvm-14-dev clang-14 linux-headers-`uname -r` linux-tools-`uname -r` libbpf-dev libbpf0
ln -s `which clang-14` /usr/bin/clang
ln -s `which llvm-strip-14` /usr/bin/llvm-strip
常用命令:
生成 vmlinux.h
头文件:
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
列出包含 sleep 的所有探针(需要安装 bpftrace):
bpftrace -l '*sleep*'
列出加载的 eBPF 程序:
bpftool prog show
wget https://go.dev/dl/go1.21.10.linux-amd64.tar.gz
tar -xf go1.21.10.linux-amd64.tar.gz
mv go /usr/local/
# 在 /etc/profile 中增加如下内容
export GOROOT=/usr/local/go
export GOPATH=/root/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
source /etc/profile
go env -w GOPROXY=https://goproxy.cn,direct
# 安装 bpf2go
go install github.com/cilium/ebpf/cmd/bpf2go@v0.14.0
bpf2go -h
本示例来自于 cilium/ebpf。
mkdir ebpftest
cd ebpftest/
go mod init ebpftest
go get github.com/cilium/ebpf@v0.14.0
kprobe_percpu.c:
//go:build ignore
#include "common.h"
char __license[] SEC("license") = "Dual MIT/GPL";
struct bpf_map_def SEC("maps") kprobe_map = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(u64),
.max_entries = 1,
};
SEC("kprobe/sys_execve")
int kprobe_execve() {
u32 key = 0;
u64 initval = 1, *valp;
valp = bpf_map_lookup_elem(&kprobe_map, &key);
if (!valp) {
bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);
return 0;
}
__sync_fetch_and_add(valp, 1);
return 0;
}
// This program demonstrates attaching an eBPF program to a kernel symbol and
// using percpu map to collect data. The eBPF program will be attached to the
// start of the sys_execve kernel function and prints out the number of called
// times on each cpu every second.
package main
import (
"log"
"time"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/rlimit"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf kprobe_percpu.c -- -I../headers
const mapKey uint32 = 0
func main() {
// Name of the kernel function to trace.
fn := "sys_execve"
// Allow the current process to lock memory for eBPF resources.
if err := rlimit.RemoveMemlock(); err != nil {
log.Fatal(err)
}
// Load pre-compiled programs and maps into the kernel.
objs := bpfObjects{}
if err := loadBpfObjects(&objs, nil); err != nil {
log.Fatalf("loading objects: %v", err)
}
defer objs.Close()
// Open a Kprobe at the entry point of the kernel function and attach the
// pre-compiled program. Each time the kernel function enters, the program
// will increment the execution counter by 1. The read loop below polls this
// map value once per second.
kp, err := link.Kprobe(fn, objs.KprobeExecve, nil)
if err != nil {
log.Fatalf("opening kprobe: %s", err)
}
defer kp.Close()
// Read loop reporting the total amount of times the kernel
// function was entered, once per second.
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
log.Println("Waiting for events..")
for range ticker.C {
var all_cpu_value []uint64
if err := objs.KprobeMap.Lookup(mapKey, &all_cpu_value); err != nil {
log.Fatalf("reading map: %v", err)
}
for cpuid, cpuvalue := range all_cpu_value {
log.Printf("%s called %d times on CPU%v\n", fn, cpuvalue, cpuid)
}
log.Printf("\n")
}
}
将 https://github.com/cilium/ebpf/tree/v0.14.0/examples/headers 目录中的头文件拷贝到项目目录的 headers/
子目录下。
修改 main.go:
//go:generate bpf2go bpf kprobe_percpu.c -- -I./headers
运行如下命令生成 eBPF 程序:
go generate
go mod tidy
go build -tags amd64 -o main
./main
打开另一个窗口,运行若干条命令,同时观察输出。
注意:
go build
中的 -tags
选项的值