proc/internal/ebpf: drop dependency on cgo (#3072)
The ebpf implementations uses cgo, but only to access some C struct definitions. Instead of using cgo simply duplicate the defintion of those two structs in Go and add a test to check that the duplicate definitions remain synchronized. Fixes #2827
This commit is contained in:
parent
6ef5284505
commit
5452c30fac
@ -41,7 +41,7 @@ GOPATH=$(pwd)/go
|
||||
export GOPATH
|
||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
||||
go version
|
||||
go install honnef.co/go/tools/cmd/staticcheck@2021.1.1 || true
|
||||
go install honnef.co/go/tools/cmd/staticcheck@2022.1.2 || true
|
||||
|
||||
uname -a
|
||||
echo "$PATH"
|
||||
|
@ -1182,10 +1182,6 @@ func TestVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStaticcheck(t *testing.T) {
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
|
||||
//TODO(aarzilli): remove this before version 1.8.0 is released
|
||||
t.Skip("staticcheck does not currently support Go 1.18")
|
||||
}
|
||||
_, err := exec.LookPath("staticcheck")
|
||||
if err != nil {
|
||||
t.Skip("staticcheck not installed")
|
||||
|
@ -150,9 +150,7 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
|
||||
for len(mem) > 0 {
|
||||
bp, atbp := breakpoints.M[pc]
|
||||
if atbp {
|
||||
for i := range bp.OriginalData {
|
||||
mem[i] = bp.OriginalData[i]
|
||||
}
|
||||
copy(mem, bp.OriginalData)
|
||||
}
|
||||
|
||||
file, line, fn := bi.PCToLine(pc)
|
||||
|
@ -1,10 +1,8 @@
|
||||
//go:build linux && amd64 && cgo && go1.16
|
||||
// +build linux,amd64,cgo,go1.16
|
||||
//go:build linux && amd64 && go1.16
|
||||
// +build linux,amd64,go1.16
|
||||
|
||||
package ebpf
|
||||
|
||||
// #include "./bpf/include/function_vals.bpf.h"
|
||||
import "C"
|
||||
import (
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
@ -22,6 +20,36 @@ import (
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
)
|
||||
|
||||
//lint:file-ignore U1000 some fields are used by the C program
|
||||
|
||||
// function_parameter_t tracks function_parameter_t from function_vals.bpf.h
|
||||
type function_parameter_t struct {
|
||||
kind uint32
|
||||
size uint32
|
||||
offset int32
|
||||
in_reg bool
|
||||
n_pieces int32
|
||||
reg_nums [6]int32
|
||||
daddr uint64
|
||||
val [0x30]byte
|
||||
deref_val [0x30]byte
|
||||
}
|
||||
|
||||
// function_parameter_list_t tracks function_parameter_list_t from function_vals.bpf.h
|
||||
type function_parameter_list_t struct {
|
||||
goid_offset uint32
|
||||
g_addr_offset uint64
|
||||
goroutine_id uint32
|
||||
fn_addr uint32
|
||||
is_ret bool
|
||||
|
||||
n_parameters uint32
|
||||
params [6]function_parameter_t
|
||||
|
||||
n_ret_parameters uint32
|
||||
ret_params [6]function_parameter_t
|
||||
}
|
||||
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags "go1.16" -target amd64 trace bpf/trace.bpf.c -- -I./bpf/include
|
||||
|
||||
const FakeAddressBase = 0xbeed000000000000
|
||||
@ -56,7 +84,7 @@ func (ctx *EBPFContext) UpdateArgMap(key uint64, goidOffset int64, args []UProbe
|
||||
return errors.New("eBPF map not loaded")
|
||||
}
|
||||
params := createFunctionParameterList(key, goidOffset, args, isret)
|
||||
params.g_addr_offset = C.longlong(gAddrOffset)
|
||||
params.g_addr_offset = gAddrOffset
|
||||
return ctx.bpfArgMap.Update(unsafe.Pointer(&key), unsafe.Pointer(¶ms), ebpf.UpdateAny)
|
||||
}
|
||||
|
||||
@ -118,7 +146,7 @@ func LoadEBPFTracingProgram(path string) (*EBPFContext, error) {
|
||||
}
|
||||
|
||||
func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
|
||||
params := (*C.function_parameter_list_t)(unsafe.Pointer(&rawParamBytes[0]))
|
||||
params := (*function_parameter_list_t)(unsafe.Pointer(&rawParamBytes[0]))
|
||||
|
||||
defer runtime.KeepAlive(params) // Ensure the param is not garbage collected.
|
||||
|
||||
@ -126,14 +154,14 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
|
||||
rawParams.FnAddr = int(params.fn_addr)
|
||||
rawParams.GoroutineID = int(params.goroutine_id)
|
||||
|
||||
parseParam := func(param C.function_parameter_t) *RawUProbeParam {
|
||||
parseParam := func(param function_parameter_t) *RawUProbeParam {
|
||||
iparam := &RawUProbeParam{}
|
||||
data := make([]byte, 0x60)
|
||||
ret := param
|
||||
iparam.Kind = reflect.Kind(ret.kind)
|
||||
|
||||
val := C.GoBytes(unsafe.Pointer(&ret.val), C.int(ret.size))
|
||||
rawDerefValue := C.GoBytes(unsafe.Pointer(&ret.deref_val[0]), 0x30)
|
||||
val := ret.val[:ret.size]
|
||||
rawDerefValue := ret.deref_val[:0x30]
|
||||
copy(data, val)
|
||||
copy(data[0x30:], rawDerefValue)
|
||||
iparam.Data = data
|
||||
@ -166,26 +194,26 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
|
||||
return rawParams
|
||||
}
|
||||
|
||||
func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) C.function_parameter_list_t {
|
||||
var params C.function_parameter_list_t
|
||||
params.goid_offset = C.uint(goidOffset)
|
||||
params.fn_addr = C.uint(entry)
|
||||
params.is_ret = C.bool(isret)
|
||||
params.n_parameters = C.uint(0)
|
||||
params.n_ret_parameters = C.uint(0)
|
||||
func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) function_parameter_list_t {
|
||||
var params function_parameter_list_t
|
||||
params.goid_offset = uint32(goidOffset)
|
||||
params.fn_addr = uint32(entry)
|
||||
params.is_ret = isret
|
||||
params.n_parameters = 0
|
||||
params.n_ret_parameters = 0
|
||||
for _, arg := range args {
|
||||
var param C.function_parameter_t
|
||||
param.size = C.uint(arg.Size)
|
||||
param.offset = C.int(arg.Offset)
|
||||
param.kind = C.uint(arg.Kind)
|
||||
var param function_parameter_t
|
||||
param.size = uint32(arg.Size)
|
||||
param.offset = int32(arg.Offset)
|
||||
param.kind = uint32(arg.Kind)
|
||||
if arg.InReg {
|
||||
param.in_reg = true
|
||||
param.n_pieces = C.int(len(arg.Pieces))
|
||||
param.n_pieces = int32(len(arg.Pieces))
|
||||
for i := range arg.Pieces {
|
||||
if i > 5 {
|
||||
break
|
||||
}
|
||||
param.reg_nums[i] = C.int(arg.Pieces[i])
|
||||
param.reg_nums[i] = int32(arg.Pieces[i])
|
||||
}
|
||||
}
|
||||
if !arg.Ret {
|
||||
|
@ -1,5 +1,5 @@
|
||||
//go:build !linux || !amd64 || !go1.16 || !cgo
|
||||
// +build !linux !amd64 !go1.16 !cgo
|
||||
//go:build !linux || !amd64 || !go1.16
|
||||
// +build !linux !amd64 !go1.16
|
||||
|
||||
package ebpf
|
||||
|
||||
|
43
pkg/proc/internal/ebpf/helpers_test.go
Normal file
43
pkg/proc/internal/ebpf/helpers_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
//go:build linux && amd64 && cgo && go1.16
|
||||
// +build linux,amd64,cgo,go1.16
|
||||
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc/internal/ebpf/testhelper"
|
||||
)
|
||||
|
||||
func compareStructTypes(t *testing.T, gostructVal, cstructVal interface{}) {
|
||||
gostruct := reflect.ValueOf(gostructVal).Type()
|
||||
cstruct := reflect.ValueOf(cstructVal).Type()
|
||||
if gostruct.NumField() != cstruct.NumField() {
|
||||
t.Errorf("mismatched field number %d %d", gostruct.NumField(), cstruct.NumField())
|
||||
return
|
||||
}
|
||||
for i := 0; i < cstruct.NumField(); i++ {
|
||||
gofield := gostruct.Field(i)
|
||||
cfield := cstruct.Field(i)
|
||||
t.Logf("%d %s %s\n", i, gofield.Name, cfield.Name)
|
||||
if gofield.Name != cfield.Name {
|
||||
t.Errorf("mismatched name for field %s %s", gofield.Name, cfield.Name)
|
||||
}
|
||||
if gofield.Offset != cfield.Offset {
|
||||
t.Errorf("mismatched offset for field %s %s (%d %d)", gofield.Name, cfield.Name, gofield.Offset, cfield.Offset)
|
||||
}
|
||||
if gofield.Type.Size() != cfield.Type.Size() {
|
||||
t.Errorf("mismatched size for field %s %s (%d %d)", gofield.Name, cfield.Name, gofield.Type.Size(), cfield.Type.Size())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructConsistency(t *testing.T) {
|
||||
t.Run("function_parameter_t", func(t *testing.T) {
|
||||
compareStructTypes(t, function_parameter_t{}, testhelper.Function_parameter_t{})
|
||||
})
|
||||
t.Run("function_parameter_list_t", func(t *testing.T) {
|
||||
compareStructTypes(t, function_parameter_list_t{}, testhelper.Function_parameter_list_t{})
|
||||
})
|
||||
}
|
13
pkg/proc/internal/ebpf/testhelper/testhelper.go
Normal file
13
pkg/proc/internal/ebpf/testhelper/testhelper.go
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build linux && amd64 && cgo && go1.16
|
||||
// +build linux,amd64,cgo,go1.16
|
||||
|
||||
package testhelper
|
||||
|
||||
// #include "../bpf/include/function_vals.bpf.h"
|
||||
import "C"
|
||||
|
||||
// Function_parameter_t exports function_parameter_t from function_vals.bpf.h
|
||||
type Function_parameter_t C.function_parameter_t
|
||||
|
||||
// Function_parameter_list_t exports function_parameter_list_t from function_vals.bpf.h
|
||||
type Function_parameter_list_t C.function_parameter_list_t
|
Loading…
Reference in New Issue
Block a user