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 GOPATH
|
||||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
||||||
go version
|
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
|
uname -a
|
||||||
echo "$PATH"
|
echo "$PATH"
|
||||||
|
@ -1182,10 +1182,6 @@ func TestVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStaticcheck(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")
|
_, err := exec.LookPath("staticcheck")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skip("staticcheck not installed")
|
t.Skip("staticcheck not installed")
|
||||||
|
@ -150,9 +150,7 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
|
|||||||
for len(mem) > 0 {
|
for len(mem) > 0 {
|
||||||
bp, atbp := breakpoints.M[pc]
|
bp, atbp := breakpoints.M[pc]
|
||||||
if atbp {
|
if atbp {
|
||||||
for i := range bp.OriginalData {
|
copy(mem, bp.OriginalData)
|
||||||
mem[i] = bp.OriginalData[i]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file, line, fn := bi.PCToLine(pc)
|
file, line, fn := bi.PCToLine(pc)
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
//go:build linux && amd64 && cgo && go1.16
|
//go:build linux && amd64 && go1.16
|
||||||
// +build linux,amd64,cgo,go1.16
|
// +build linux,amd64,go1.16
|
||||||
|
|
||||||
package ebpf
|
package ebpf
|
||||||
|
|
||||||
// #include "./bpf/include/function_vals.bpf.h"
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@ -22,6 +20,36 @@ import (
|
|||||||
"github.com/cilium/ebpf/ringbuf"
|
"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
|
//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
|
const FakeAddressBase = 0xbeed000000000000
|
||||||
@ -56,7 +84,7 @@ func (ctx *EBPFContext) UpdateArgMap(key uint64, goidOffset int64, args []UProbe
|
|||||||
return errors.New("eBPF map not loaded")
|
return errors.New("eBPF map not loaded")
|
||||||
}
|
}
|
||||||
params := createFunctionParameterList(key, goidOffset, args, isret)
|
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)
|
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 {
|
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.
|
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.FnAddr = int(params.fn_addr)
|
||||||
rawParams.GoroutineID = int(params.goroutine_id)
|
rawParams.GoroutineID = int(params.goroutine_id)
|
||||||
|
|
||||||
parseParam := func(param C.function_parameter_t) *RawUProbeParam {
|
parseParam := func(param function_parameter_t) *RawUProbeParam {
|
||||||
iparam := &RawUProbeParam{}
|
iparam := &RawUProbeParam{}
|
||||||
data := make([]byte, 0x60)
|
data := make([]byte, 0x60)
|
||||||
ret := param
|
ret := param
|
||||||
iparam.Kind = reflect.Kind(ret.kind)
|
iparam.Kind = reflect.Kind(ret.kind)
|
||||||
|
|
||||||
val := C.GoBytes(unsafe.Pointer(&ret.val), C.int(ret.size))
|
val := ret.val[:ret.size]
|
||||||
rawDerefValue := C.GoBytes(unsafe.Pointer(&ret.deref_val[0]), 0x30)
|
rawDerefValue := ret.deref_val[:0x30]
|
||||||
copy(data, val)
|
copy(data, val)
|
||||||
copy(data[0x30:], rawDerefValue)
|
copy(data[0x30:], rawDerefValue)
|
||||||
iparam.Data = data
|
iparam.Data = data
|
||||||
@ -166,26 +194,26 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
|
|||||||
return rawParams
|
return rawParams
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) C.function_parameter_list_t {
|
func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) function_parameter_list_t {
|
||||||
var params C.function_parameter_list_t
|
var params function_parameter_list_t
|
||||||
params.goid_offset = C.uint(goidOffset)
|
params.goid_offset = uint32(goidOffset)
|
||||||
params.fn_addr = C.uint(entry)
|
params.fn_addr = uint32(entry)
|
||||||
params.is_ret = C.bool(isret)
|
params.is_ret = isret
|
||||||
params.n_parameters = C.uint(0)
|
params.n_parameters = 0
|
||||||
params.n_ret_parameters = C.uint(0)
|
params.n_ret_parameters = 0
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
var param C.function_parameter_t
|
var param function_parameter_t
|
||||||
param.size = C.uint(arg.Size)
|
param.size = uint32(arg.Size)
|
||||||
param.offset = C.int(arg.Offset)
|
param.offset = int32(arg.Offset)
|
||||||
param.kind = C.uint(arg.Kind)
|
param.kind = uint32(arg.Kind)
|
||||||
if arg.InReg {
|
if arg.InReg {
|
||||||
param.in_reg = true
|
param.in_reg = true
|
||||||
param.n_pieces = C.int(len(arg.Pieces))
|
param.n_pieces = int32(len(arg.Pieces))
|
||||||
for i := range arg.Pieces {
|
for i := range arg.Pieces {
|
||||||
if i > 5 {
|
if i > 5 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.reg_nums[i] = C.int(arg.Pieces[i])
|
param.reg_nums[i] = int32(arg.Pieces[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !arg.Ret {
|
if !arg.Ret {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//go:build !linux || !amd64 || !go1.16 || !cgo
|
//go:build !linux || !amd64 || !go1.16
|
||||||
// +build !linux !amd64 !go1.16 !cgo
|
// +build !linux !amd64 !go1.16
|
||||||
|
|
||||||
package ebpf
|
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