how to get goroutine id with ebpf

417 views Asked by At

I use cilium ebpf pakage to write a ebpf program for getting the goroutine id. but failed. my uprobe.c like this : I think the key problem is that golang struct g trans to goroutine.h is wrong. can anyone help?

uprobe.c

SEC("uprobe/runtime.newproc1")
int uprobe_runtime_newproc1(struct pt_regs *ctx) {
    u32 key     = 2;
    u64 initval = 1, *valp;

    valp = bpf_map_lookup_elem(&uprobe_map, &key);
    if (!valp) {
        bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
        return 0;
    }
    __sync_fetch_and_add(valp, 1);

    struct g* goroutine_struct = (void *)PT_REGS_PARM4(ctx);

    // retrieve output parameter
    s64 goid = 0;
    bpf_probe_read(&goid, sizeof(goid), &goroutine_struct->goid);

    bpf_printk("bpf_printk bpf_probe_read goroutine_struct->goid: %lld", goid);


    struct g gs;
    bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM4(ctx));
    bpf_printk("bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);

    // test
    void* ptr = (void *)PT_REGS_PARM4(ctx);
    s64 goid2 = 0;
    bpf_probe_read(&goid2, sizeof(goid2), (void *)(ptr+152));
    bpf_printk("bpf_printk bpf_probe_read goid2: %lld", goid2);

    return 0;
}

goroutine.h

#include "common.h"



struct stack  {
    u64 lo;
    u64 hi;
};

struct gobuf  {
    u64 sp;
    u64 pc;
    u64 g;
    u64 ctxt;
    u64 ret;
    u64 lr;
    u64 bp;
};


/*
go version go1.17.2 linux/amd64

type stack struct {
    lo uintptr
    hi uintptr
}

type gobuf struct {
    sp   uintptr
    pc   uintptr
    g    uintptr
    ctxt uintptr
    ret  uintptr
    lr   uintptr
    bp   uintptr
}

type g struct { 
    stack       stack   // offset known to runtime/cgo
    stackguard0 uintptr // offset known to liblink
    stackguard1 uintptr // offset known to liblink

    _panic    *_panic // innermost panic - offset known to liblink
    _defer    *_defer // innermost defer
    m         *m      // current m; offset known to arm liblink
    sched     gobuf
    syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
    syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
    stktopsp  uintptr // expected sp at top of stack, to check in traceback
    
    param        unsafe.Pointer
    atomicstatus uint32
    stackLock    uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
    goid         int64
}

*/
struct g {
    struct stack stack;
    u64 stackguard0;
    u64 stackguard1;

    u64 _panic;
    u64 _defer;
    u64 m;
    struct gobuf sched ;
    u64 syscallsp;
    u64 syscallpc;
    u64 stktopsp;
    u64 param;
    u32 atomicstatus;
    u32 stackLock;
    s64 goid;          // Here it is!
};

When I run my program , cat /sys/kernel/debug/tracing/trace_pipe output like this , get the wrong go id:

<...>-1336127 [000] d... 20113210.986990: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct->goid: 4938558469562467144
<...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct.goid: 4938558469562467144
<...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goid2: 4938558469562467144
Blockquote
1

There are 1 answers

0
weizhao On BEST ANSWER

I found a solution:

  1. my golang version is 1.17.2, amd64. and the amd64 architecture uses the following sequence of 9 registers for integer arguments and results: RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11

  2. runtime.newproc1 which in go 1.17.2 has 5 args. callergp *g is the 4th. when I gdb my userspace program, it use rdi register to save ptr addr of callergp *g. so use PT_REGS_PARM1 is the right way. because (#define PT_REGS_PARM1(x) ((x)->rdi))

  3. after all, the code like this :

    SEC("uprobe/runtime.newproc1")
    int uprobe_runtime_newproc1(struct pt_regs *ctx) {
        u32 key     = 2;
        u64 initval = 1, *valp;
    
        valp = bpf_map_lookup_elem(&uprobe_map, &key);
        if (!valp) {
            bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
            return 0;
        }
        __sync_fetch_and_add(valp, 1);
    
        // retrieve output parameter
        struct g gs;
        bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM1(ctx));
        bpf_printk("uprobe_runtime_newproc1 bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);
    
        return 0;
    }