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