c-在Golang中获取内核符号名称



我正在尝试使用eBPF中的bpf_get_stackid来查询带有标志BPF_F_FAST_STACK_CMP的内核堆栈。

在堆栈映射(BPF_MAP_TYPE_STACK_TRACE类型(中,我可以根据堆栈ID获得符号地址列表。但当我尝试使用/proc/kallsyms来匹配它们时,它们不是同一个。我已经读取了kallsyms文件,并为id创建了第一个地址(uint64的十六进制字符串(。不知道如何解决它,而且,我正在使用cilium/ebpf作为eBPF库。

我希望能够通过Golang代码在堆栈中找到符号名称。

eBPFC代码:

struct key_t {
int kernel_stack_id;
};
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} counts SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, 100 * sizeof(u64));
__uint(max_entries, 10000);
} stacks SEC(".maps");
SEC("kprobe/blk_account_io_start")
int bpf_blk_account_io_start(struct pt_regs *ctx) {
// create map key
struct key_t key = {};
// get stacks
key.kernel_stack_id = bpf_get_stackid(ctx, &stacks, BPF_F_FAST_STACK_CMP);
bpf_perf_event_output(ctx, &counts, BPF_F_CURRENT_CPU, &key, sizeof(key));
return 0;
}

Golang代码:

type Event struct {
KernelStackId uint32
}
// Read Event
rd, _ := perf.NewReader(objs.Counts, os.Getpagesize())
record, _ := rd.Read()
binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event)
stackIdList := make([]uint64, 100)
objs.Stacks.Lookup(event.KernelStackId, &stackIdList)
// Read kernel symbols
file, err := os.Open("/proc/kallsyms")
scanner := bufio.NewScanner(file)
for scanner.Scan() {
info := strings.Split(scanner.Text(), " ")
atoi, err := strconv.ParseUint(info[0], 16, 64)
for _, addr := range stackIdList {
if atoi == addr {
fmt.Printf("Found the kernel symbol: %s", info[2])
break
}
}
}
注意:这可能是错误的

我不确定kernel/ebpf是如何组合调用堆栈的,但通常调用堆栈中的条目是从堆栈帧的返回地址收集的。这意味着这些条目代表的不是被调用函数的内存地址,而是函数被调用的位置。

因此,您可能没有试图从/proc/kallsyms中找到完全匹配的符号,而是试图找到具有最高内存地址的符号,该地址低于条目,该条目应该是调用者的符号。

参考:

  • https://en.wikipedia.org/wiki/Call_stack

因为stackIdList中的每个addr都有一个偏移量,所以无法直接比较/proc/kallsyms的地址,请参考以下bcc代码。

if (0 == bcc_symcache_resolve(bcc_symcache, ip[i], &sym)) {
debug("t0x%016lxt%-10st%-20st0x%-016lx", ip[i], sym.name, sym.module,
                              sym.offset);
}

最新更新