'Get kernel symbol name in Golang
I’m trying to use bpf_get_stackid
in the eBPF
to query the kernel stack with the flag BPF_F_FAST_STACK_CMP
.
In the stacks map(BPF_MAP_TYPE_STACK_TRACE
type), I could get the symbol address list according to stack ID. But when I try to use /proc/kallsyms
to match them, they are not the same one.
I have already read the kallsyms
file and made the first address to the id(hex string to uint64). Don’t know how to resolve it, also, I’m using the cilium/ebpf
as the eBPF library.
I want to be able to find the symbol name in the stack through the Golang code.
eBPF
C code:
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
code:
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
}
}
}
Solution 1:[1]
Caveat: this could be wrong.
I'm not sure how kernel/ebpf managed to put together the call stack, but normally entries in call stack are collected from the return addresses of stack frames. Which means those entries represent not the memory addresses of the functions that get called, but the locations where functions get called.
So probably you are not trying to find the exact match from /proc/kallsyms
, try to find the symbol with the highest memory address which is lower than the entry, which should be the symbol of the caller.
ref:
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | pctang |