github.com/undoio/delve@v1.9.0/pkg/proc/internal/ebpf/helpers.go (about) 1 //go:build linux && amd64 && cgo && go1.16 2 // +build linux,amd64,cgo,go1.16 3 4 package ebpf 5 6 // #include "./bpf/include/function_vals.bpf.h" 7 import "C" 8 import ( 9 "debug/elf" 10 "encoding/binary" 11 "errors" 12 "reflect" 13 "runtime" 14 "sync" 15 "unsafe" 16 17 "github.com/undoio/delve/pkg/dwarf/godwarf" 18 "github.com/undoio/delve/pkg/dwarf/op" 19 20 "github.com/cilium/ebpf" 21 "github.com/cilium/ebpf/link" 22 "github.com/cilium/ebpf/ringbuf" 23 ) 24 25 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags "go1.16" -target amd64 trace bpf/trace.bpf.c -- -I./bpf/include 26 27 const FakeAddressBase = 0xbeed000000000000 28 29 type EBPFContext struct { 30 objs *traceObjects 31 bpfEvents chan []byte 32 bpfRingBuf *ringbuf.Reader 33 executable *link.Executable 34 bpfArgMap *ebpf.Map 35 36 parsedBpfEvents []RawUProbeParams 37 m sync.Mutex 38 } 39 40 func (ctx *EBPFContext) Close() { 41 if ctx.objs != nil { 42 ctx.objs.Close() 43 } 44 } 45 46 func (ctx *EBPFContext) AttachUprobe(pid int, name string, offset uint64) error { 47 if ctx.executable == nil { 48 return errors.New("no eBPF program loaded") 49 } 50 _, err := ctx.executable.Uprobe(name, ctx.objs.tracePrograms.UprobeDlvTrace, &link.UprobeOptions{PID: pid, Offset: offset}) 51 return err 52 } 53 54 func (ctx *EBPFContext) UpdateArgMap(key uint64, goidOffset int64, args []UProbeArgMap, gAddrOffset uint64, isret bool) error { 55 if ctx.bpfArgMap == nil { 56 return errors.New("eBPF map not loaded") 57 } 58 params := createFunctionParameterList(key, goidOffset, args, isret) 59 params.g_addr_offset = C.longlong(gAddrOffset) 60 return ctx.bpfArgMap.Update(unsafe.Pointer(&key), unsafe.Pointer(¶ms), ebpf.UpdateAny) 61 } 62 63 func (ctx *EBPFContext) GetBufferedTracepoints() []RawUProbeParams { 64 ctx.m.Lock() 65 defer ctx.m.Unlock() 66 67 if len(ctx.parsedBpfEvents) == 0 { 68 return make([]RawUProbeParams, 0) 69 } 70 71 events := make([]RawUProbeParams, len(ctx.parsedBpfEvents)) 72 copy(events, ctx.parsedBpfEvents) 73 ctx.parsedBpfEvents = ctx.parsedBpfEvents[:0] 74 return events 75 } 76 77 func LoadEBPFTracingProgram(path string) (*EBPFContext, error) { 78 var ( 79 ctx EBPFContext 80 err error 81 objs traceObjects 82 ) 83 84 ctx.executable, err = link.OpenExecutable(path) 85 if err != nil { 86 return nil, err 87 } 88 89 if err := loadTraceObjects(&objs, nil); err != nil { 90 return nil, err 91 } 92 ctx.objs = &objs 93 94 ctx.bpfRingBuf, err = ringbuf.NewReader(objs.Events) 95 if err != nil { 96 return nil, err 97 } 98 99 ctx.bpfArgMap = objs.ArgMap 100 101 // TODO(derekparker): This should eventually be moved to a more generalized place. 102 go func() { 103 for { 104 e, err := ctx.bpfRingBuf.Read() 105 if err != nil { 106 return 107 } 108 109 parsed := parseFunctionParameterList(e.RawSample) 110 111 ctx.m.Lock() 112 ctx.parsedBpfEvents = append(ctx.parsedBpfEvents, parsed) 113 ctx.m.Unlock() 114 } 115 }() 116 117 return &ctx, nil 118 } 119 120 func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams { 121 params := (*C.function_parameter_list_t)(unsafe.Pointer(&rawParamBytes[0])) 122 123 defer runtime.KeepAlive(params) // Ensure the param is not garbage collected. 124 125 var rawParams RawUProbeParams 126 rawParams.FnAddr = int(params.fn_addr) 127 rawParams.GoroutineID = int(params.goroutine_id) 128 129 parseParam := func(param C.function_parameter_t) *RawUProbeParam { 130 iparam := &RawUProbeParam{} 131 data := make([]byte, 0x60) 132 ret := param 133 iparam.Kind = reflect.Kind(ret.kind) 134 135 val := C.GoBytes(unsafe.Pointer(&ret.val), C.int(ret.size)) 136 rawDerefValue := C.GoBytes(unsafe.Pointer(&ret.deref_val[0]), 0x30) 137 copy(data, val) 138 copy(data[0x30:], rawDerefValue) 139 iparam.Data = data 140 141 pieces := make([]op.Piece, 0, 2) 142 pieces = append(pieces, op.Piece{Size: 0x30, Kind: op.AddrPiece, Val: FakeAddressBase}) 143 pieces = append(pieces, op.Piece{Size: 0x30, Kind: op.AddrPiece, Val: FakeAddressBase + 0x30}) 144 iparam.Pieces = pieces 145 146 iparam.Addr = FakeAddressBase 147 148 switch iparam.Kind { 149 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 150 iparam.RealType = &godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8}}} 151 case reflect.String: 152 strLen := binary.LittleEndian.Uint64(val[8:]) 153 iparam.Base = FakeAddressBase + 0x30 154 iparam.Len = int64(strLen) 155 } 156 return iparam 157 } 158 159 for i := 0; i < int(params.n_parameters); i++ { 160 rawParams.InputParams = append(rawParams.InputParams, parseParam(params.params[i])) 161 } 162 for i := 0; i < int(params.n_ret_parameters); i++ { 163 rawParams.ReturnParams = append(rawParams.ReturnParams, parseParam(params.ret_params[i])) 164 } 165 166 return rawParams 167 } 168 169 func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) C.function_parameter_list_t { 170 var params C.function_parameter_list_t 171 params.goid_offset = C.uint(goidOffset) 172 params.fn_addr = C.uint(entry) 173 params.is_ret = C.bool(isret) 174 params.n_parameters = C.uint(0) 175 params.n_ret_parameters = C.uint(0) 176 for _, arg := range args { 177 var param C.function_parameter_t 178 param.size = C.uint(arg.Size) 179 param.offset = C.int(arg.Offset) 180 param.kind = C.uint(arg.Kind) 181 if arg.InReg { 182 param.in_reg = true 183 param.n_pieces = C.int(len(arg.Pieces)) 184 for i := range arg.Pieces { 185 if i > 5 { 186 break 187 } 188 param.reg_nums[i] = C.int(arg.Pieces[i]) 189 } 190 } 191 if !arg.Ret { 192 params.params[params.n_parameters] = param 193 params.n_parameters++ 194 } else { 195 params.ret_params[params.n_ret_parameters] = param 196 params.n_ret_parameters++ 197 } 198 } 199 return params 200 } 201 202 func AddressToOffset(f *elf.File, addr uint64) (uint64, error) { 203 sectionsToSearchForSymbol := []*elf.Section{} 204 205 for i := range f.Sections { 206 if f.Sections[i].Flags == elf.SHF_ALLOC+elf.SHF_EXECINSTR { 207 sectionsToSearchForSymbol = append(sectionsToSearchForSymbol, f.Sections[i]) 208 } 209 } 210 211 var executableSection *elf.Section 212 213 // Find what section the symbol is in by checking the executable section's 214 // addr space. 215 for m := range sectionsToSearchForSymbol { 216 if addr > sectionsToSearchForSymbol[m].Addr && 217 addr < sectionsToSearchForSymbol[m].Addr+sectionsToSearchForSymbol[m].Size { 218 executableSection = sectionsToSearchForSymbol[m] 219 } 220 } 221 222 if executableSection == nil { 223 return 0, errors.New("could not find symbol in executable sections of binary") 224 } 225 226 return uint64(addr - executableSection.Addr + executableSection.Offset), nil 227 }