简体   繁体   中英

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
        }
    }
}

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:

Because each addr in the stackIdList has an offset, you cannot directly compare the addresses of /proc/kallsyms,Please refer to the following bcc codes.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM