Open BruceChen7 opened 1 year ago
// main_amd64.go // SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2021 Authors of Cilium */ //go:generate sh -c "echo Generating for amd64" //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang KProbePWRU ./bpf/kprobe_pwru.c -- -DOUTPUT_SKB -D__TARGET_ARCH_x86 -I./bpf/headers -Wno-address-of-packed-member //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang KProbeMultiPWRU ./bpf/kprobe_pwru.c -- -DOUTPUT_SKB -DHAS_KPROBE_MULTI -D__TARGET_ARCH_x86 -I./bpf/headers -Wno-address-of-packed-member //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang KProbePWRUWithoutOutputSKB ./bpf/kprobe_pwru.c -- -D__TARGET_ARCH_x86 -I./bpf/headers -Wno-address-of-packed-member //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang KProbeMultiPWRUWithoutOutputSKB ./bpf/kprobe_pwru.c -- -D HAS_KPROBE_MULTI -D__TARGET_ARCH_x86 -I./bpf/headers -Wno-address-of-packed-member //go:generate go run ./tools/getgetter.go -struct ^(KProbePWRU|KProbeMultiPWRU|KProbePWRUWithoutOutputSKB|KProbeMultiPWRUWithoutOutputSKB)(Programs|Maps)$ package main
最后一行是生成脚手架代码,准确来说,是 xxx_getter.go,包含了小端和大端代码
// Code generated by getgetter; DO NOT EDIT. //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 // +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 package main import "github.com/cilium/ebpf" func (k *KProbeMultiPWRUMaps) GetEvents() *ebpf.Map { return k.Events } func (k *KProbeMultiPWRUMaps) GetPrintSkbMap() *ebpf.Map { return k.PrintSkbMap } func (k *KProbeMultiPWRUMaps) GetPrintStackMap() *ebpf.Map { return k.PrintStackMap } func (k *KProbeMultiPWRUPrograms) GetKprobeSkb1() *ebpf.Program { return k.KprobeSkb1 } func (k *KProbeMultiPWRUPrograms) GetKprobeSkb2() *ebpf.Program { return k.KprobeSkb2 } func (k *KProbeMultiPWRUPrograms) GetKprobeSkb3() *ebpf.Program { return k.KprobeSkb3 } func (k *KProbeMultiPWRUPrograms) GetKprobeSkb4() *ebpf.Program { return k.KprobeSkb4 } func (k *KProbeMultiPWRUPrograms) GetKprobeSkb5() *ebpf.Program { return k.KprobeSkb5 }
#ifdef HAS_KPROBE_MULTI #define PWRU_KPROBE_TYPE "kprobe.multi" #define PWRU_HAS_GET_FUNC_IP true #else #define PWRU_KPROBE_TYPE "kprobe" #define PWRU_HAS_GET_FUNC_IP false #endif /* HAS_KPROBE_MULTI */ // 添加相关的探针 #define PWRU_ADD_KPROBE(X) \ SEC(PWRU_KPROBE_TYPE "/skb-" #X) \ int kprobe_skb_##X(struct pt_regs *ctx) { \ struct sk_buff *skb = (struct sk_buff *) PT_REGS_PARM##X(ctx); \ return handle_everything(skb, ctx, PWRU_HAS_GET_FUNC_IP); \ } PWRU_ADD_KPROBE(1) PWRU_ADD_KPROBE(2) PWRU_ADD_KPROBE(3) PWRU_ADD_KPROBE(4) PWRU_ADD_KPROBE(5) #undef PWRU_KPROBE #undef PWRU_HAS_GET_FUNC_IP #undef PWRU_KPROBE_TYPE char __license[] SEC("license") = "GPL";
SEC("kprobe/skb-1") int kprobe_skb_1(struct pt_regs *ctx) { struct sk_buff *skb = (struct sk_buff *) PT_REGS_PARM1(ctx); return handle_everything(skb, ctx, PWRU_HAS_GET_FUNC_IP); }
struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY) } events SEC(".maps") struct { __uint(type, BPF_MAP_TYPE_STACK_TRACE); __uint(max_entries, 256); __uint(key_size, sizeof(u32)); __uint(value_size, MAX_STACK_DEPTH * sizeof(u64)); } print_stack_map SEC(".maps");
定义完成后,bpf2go,会根据定义的 map 和 kprobe 为,给 go 使用
type KProbeMultiPWRUMapSpecs struct { Events *ebpf.MapSpec `ebpf:"events"` PrintSkbMap *ebpf.MapSpec `ebpf:"print_skb_map"` PrintStackMap *ebpf.MapSpec `ebpf:"print_stack_map"` }
其中结构体中的前缀是根据前面 makefile 中的指定的模块来获取的
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang KProbePWRU ./bpf/kprobe_pwru.c -- -DOUTPUT_SKB -D__TARGET_ARCH_x86 -I./bpf/headers -Wno-address-of-packed-member
从 Golang 端看整个 probe 和 map
type KProbeMaps interface { GetEvents() *ebpf.Map GetPrintStack() *ebpf.Map } type KProbeMapsWithOutputSKB interface { KProbeMaps GetPrintSkbMap() *ebpf.Map } type KProbeProgrames interface { GetKprobeSkb1() *ebpf.Program GetKprobeSkb2() *ebpf.Program GetKprobeSkb3() *ebpf.Program GetKprobeSkb4() *ebpf.Program GetKprobeSkb5() *ebpf.Program } type KProbeObject interface { KprobeMaps KProbeProgrames Close() error }
从整个程序上来看,go 实际上将整个程序看成 program, map, objects 的接口,在运行时初始化成 c 对应的二进制文件
var bpfSpec *ebpf.CollectionSpec var objs pwru.KProbeObjects ... // LoadXXX 是加载的中间文件 bpfSpec, err = LoadKProbeMultiPWRU() // obj 是接口,复制为生成的 obj objs = &KProbeMultiPWRUObjects{} // 绑定 if err := bpfSpec.LoadAndAssign(objs, &opts); err != nil { log.Fatalf("Failed to load objects: %v", err) } ...
static __noinline int handle_everything(struct sk_buff *skb, struct pt_regs *ctx, bool has_get_func_ip) { struct event_t event = {}; if (cfg->is_set) { if (!filter(skb)) { return 0; } set_output(ctx, skb, &event); } event.pid = bpf_get_current_pid_tgid(); event.addr = has_get_func_ip ? bpf_get_func_ip(ctx) : PT_REGS_IP(ctx); event.skb_addr = (u64) skb; event.ts = bpf_ktime_get_ns(); event.cpu_id = bpf_get_smp_processor_id(); event.param_second = PT_REGS_PARM2(ctx); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); return 0; }
参考资料
(usage:: C 代码如何与 go 代码联动)
最后一行是生成脚手架代码,准确来说,是 xxx_getter.go,包含了小端和大端代码
定义探针
定义 map
定义完成后,bpf2go,会根据定义的 map 和 kprobe 为,给 go 使用
其中结构体中的前缀是根据前面 makefile 中的指定的模块来获取的
从 Golang 端看整个 probe 和 map
从整个程序上来看,go 实际上将整个程序看成 program, map, objects 的接口,在运行时初始化成 c 对应的二进制文件
主要逻辑
type/code #type/ebpf #public