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(&params), 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  }