pkg/proc: extend feature to print procedure parameters in trace on eBPF backend (#4305)

* Extend feature to print procedure parameters in trace on eBPF backend
Fixes #4266

* address review comments

* ran go fmt
This commit is contained in:
Archana Ravindar 2026-04-24 00:22:31 +05:30 committed by GitHub
parent 08ef5f3d75
commit 29aa227c5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 314 additions and 126 deletions

View File

@ -56,6 +56,41 @@ func assertNoError(err error, t testing.TB, s string) {
}
}
// preCondEBPFTest skips the test if eBPF testing requirements are not met.
// eBPF tests require: Linux/amd64, Go 1.16+, root privileges, and BTF kernel support.
func preCondEBPFTest(t *testing.T) {
t.Helper()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
}
// filterProcessExitLines removes "Process <pid> has exited with status" lines from output.
// This is useful when comparing trace outputs from different backends that use different PIDs.
func filterProcessExitLines(output []byte) []byte {
lines := bytes.Split(output, []byte("\n"))
var filtered [][]byte
for _, line := range lines {
if !bytes.HasPrefix(line, []byte("Process ")) || !bytes.Contains(line, []byte("has exited with status")) {
filtered = append(filtered, line)
}
}
return bytes.Join(filtered, []byte("\n"))
}
func TestBuild(t *testing.T) {
t.Parallel()
const listenAddr = "127.0.0.1:40573"
@ -1172,22 +1207,7 @@ func TestTracePrintStack(t *testing.T) {
func TestTraceEBPF(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
@ -1212,22 +1232,7 @@ func TestTraceEBPF(t *testing.T) {
func TestTraceEBPF2(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
@ -1273,22 +1278,7 @@ func TestTraceEBPF2(t *testing.T) {
func TestTraceEBPF3(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
@ -1332,22 +1322,7 @@ func TestTraceEBPF3(t *testing.T) {
func TestTraceEBPF4(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
@ -1391,22 +1366,7 @@ func TestTraceEBPF4(t *testing.T) {
func TestTraceBackendParity(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
@ -1444,17 +1404,6 @@ func TestTraceBackendParity(t *testing.T) {
}
// Filter out process exit messages which contain different PIDs
filterProcessExitLines := func(output []byte) []byte {
lines := bytes.Split(output, []byte("\n"))
var filtered [][]byte
for _, line := range lines {
if !bytes.HasPrefix(line, []byte("Process ")) || !bytes.Contains(line, []byte("has exited with status")) {
filtered = append(filtered, line)
}
}
return bytes.Join(filtered, []byte("\n"))
}
ptraceFiltered := filterProcessExitLines(ptraceOutput)
ebpfFiltered := filterProcessExitLines(ebpfOutput)
@ -1466,22 +1415,7 @@ func TestTraceBackendParity(t *testing.T) {
func TestTraceEBPFTypes(t *testing.T) {
t.Parallel()
if os.Getenv("CI") == "true" {
t.Skip("cannot run test in CI, requires kernel compiled with btf support")
}
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("not implemented on non linux/amd64 systems")
}
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
t.Skip("requires at least Go 1.16 to run test")
}
usr, err := user.Current()
if err != nil {
t.Fatal(err)
}
if usr.Uid != "0" {
t.Skip("test must be run as root")
}
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
@ -1536,6 +1470,229 @@ func TestTraceEBPFTypes(t *testing.T) {
})
}
func TestTraceVerbosityBackendParityLevel0(t *testing.T) {
t.Parallel()
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
fixturePath := filepath.Join(fixtures, "traceverb.go")
tmpDir := t.TempDir()
// Run trace with ptrace backend at verbosity level 0
// Test primitives from traceverb.go
ptraceCmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(tmpDir, "__debug_ptrace"),
"--verbose", "0", fixturePath, "main.testPrimitives")
ptraceStderr, err := ptraceCmd.StderrPipe()
assertNoError(err, t, "ptrace stderr pipe")
defer ptraceStderr.Close()
assertNoError(ptraceCmd.Start(), t, "running ptrace trace")
ptraceOutput, err := io.ReadAll(ptraceStderr)
assertNoError(err, t, "reading ptrace output")
ptraceCmd.Wait()
if len(ptraceOutput) == 0 {
t.Fatal("ptrace backend produced no output")
}
// Run trace with eBPF backend at verbosity level 0
// Test primitives from traceverb.go
ebpfCmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(tmpDir, "__debug_ebpf"),
"--verbose", "0", fixturePath, "main.testPrimitives")
ebpfStderr, err := ebpfCmd.StderrPipe()
assertNoError(err, t, "ebpf stderr pipe")
defer ebpfStderr.Close()
assertNoError(ebpfCmd.Start(), t, "running ebpf trace")
ebpfOutput, err := io.ReadAll(ebpfStderr)
assertNoError(err, t, "reading ebpf output")
ebpfCmd.Wait()
if len(ebpfOutput) == 0 {
t.Fatal("ebpf backend produced no output")
}
// Filter out process exit messages which contain different PIDs
ptraceFiltered := filterProcessExitLines(ptraceOutput)
ebpfFiltered := filterProcessExitLines(ebpfOutput)
// Compare outputs byte-for-byte
if !bytes.Equal(ptraceFiltered, ebpfFiltered) {
t.Fatalf("Output mismatch between ptrace and ebpf backends at verbosity level 0:\n\nPtrace output:\n%s\n\neBPF output:\n%s",
string(ptraceOutput), string(ebpfOutput))
}
}
func TestTraceVerbosityBackendParityLevel1(t *testing.T) {
t.Parallel()
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
fixturePath := filepath.Join(fixtures, "ebpf_trace_types.go")
tmpDir := t.TempDir()
// Run trace with ptrace backend at verbosity level 1
// Test small integer types from ebpf_trace_types.go (newly supported in eBPF)
ptraceCmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(tmpDir, "__debug_ptrace"),
"--verbose", "1", fixturePath, "main.tracedSmallInts")
ptraceStderr, err := ptraceCmd.StderrPipe()
assertNoError(err, t, "ptrace stderr pipe")
defer ptraceStderr.Close()
assertNoError(ptraceCmd.Start(), t, "running ptrace trace")
ptraceOutput, err := io.ReadAll(ptraceStderr)
assertNoError(err, t, "reading ptrace output")
ptraceCmd.Wait()
if len(ptraceOutput) == 0 {
t.Fatal("ptrace backend produced no output")
}
// Run trace with eBPF backend at verbosity level 1
// Test small integer types from ebpf_trace_types.go (newly supported in eBPF)
ebpfCmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(tmpDir, "__debug_ebpf"),
"--verbose", "1", fixturePath, "main.tracedSmallInts")
ebpfStderr, err := ebpfCmd.StderrPipe()
assertNoError(err, t, "ebpf stderr pipe")
defer ebpfStderr.Close()
assertNoError(ebpfCmd.Start(), t, "running ebpf trace")
ebpfOutput, err := io.ReadAll(ebpfStderr)
assertNoError(err, t, "reading ebpf output")
ebpfCmd.Wait()
if len(ebpfOutput) == 0 {
t.Fatal("ebpf backend produced no output")
}
// Filter out process exit messages
ptraceFiltered := filterProcessExitLines(ptraceOutput)
ebpfFiltered := filterProcessExitLines(ebpfOutput)
// Compare outputs byte-for-byte
if !bytes.Equal(ptraceFiltered, ebpfFiltered) {
t.Fatalf("Output mismatch between ptrace and ebpf backends at verbosity level 1:\n\nPtrace output:\n%s\n\neBPF output:\n%s",
string(ptraceOutput), string(ebpfOutput))
}
}
func TestTraceVerbosityBackendParityLevel2(t *testing.T) {
t.Parallel()
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
fixturePath := filepath.Join(fixtures, "ebpf_trace_types.go")
tmpDir := t.TempDir()
// Run trace with ptrace backend at verbosity level 2
// Test pointer types from ebpf_trace_types.go (newly supported in eBPF)
ptraceCmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(tmpDir, "__debug_ptrace"),
"--verbose", "2", fixturePath, "main.tracedPointer")
ptraceStderr, err := ptraceCmd.StderrPipe()
assertNoError(err, t, "ptrace stderr pipe")
defer ptraceStderr.Close()
assertNoError(ptraceCmd.Start(), t, "running ptrace trace")
ptraceOutput, err := io.ReadAll(ptraceStderr)
assertNoError(err, t, "reading ptrace output")
ptraceCmd.Wait()
if len(ptraceOutput) == 0 {
t.Fatal("ptrace backend produced no output")
}
// Run trace with eBPF backend at verbosity level 2
// Test pointer types from ebpf_trace_types.go (newly supported in eBPF)
ebpfCmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(tmpDir, "__debug_ebpf"),
"--verbose", "2", fixturePath, "main.tracedPointer")
ebpfStderr, err := ebpfCmd.StderrPipe()
assertNoError(err, t, "ebpf stderr pipe")
defer ebpfStderr.Close()
assertNoError(ebpfCmd.Start(), t, "running ebpf trace")
ebpfOutput, err := io.ReadAll(ebpfStderr)
assertNoError(err, t, "reading ebpf output")
ebpfCmd.Wait()
if len(ebpfOutput) == 0 {
t.Fatal("ebpf backend produced no output")
}
// Filter out process exit messages
ptraceFiltered := filterProcessExitLines(ptraceOutput)
ebpfFiltered := filterProcessExitLines(ebpfOutput)
// Compare outputs byte-for-byte
if !bytes.Equal(ptraceFiltered, ebpfFiltered) {
t.Fatalf("Output mismatch between ptrace and ebpf backends at verbosity level 2:\n\nPtrace output:\n%s\n\neBPF output:\n%s",
string(ptraceOutput), string(ebpfOutput))
}
}
// TestTraceVerbosityBackendParityLevel3 tests level 3.
// Note: For eBPF backend, levels 3 and 4 produce identical output because:
// - Level 3: multi-line format with short types
// - Level 4: multi-line format with full nested struct expansion
// Since eBPF can only capture primitive types, pointers (as addresses), and slices (as addresses)
// without dereferencing or reading struct fields, there is no additional nesting to expand at level 4.
// Both levels use PrettyShortenType|PrettyNewlines and differ only from level 2 by line breaks.
func TestTraceVerbosityBackendParityLevel3(t *testing.T) {
t.Parallel()
preCondEBPFTest(t)
dlvbin := protest.GetDlvBinaryEBPF(t)
fixtures := protest.FindFixturesDir()
fixturePath := filepath.Join(fixtures, "ebpf_trace_types.go")
tmpDir := t.TempDir()
// Run trace with ptrace backend at verbosity level 3
// Test small integer and slice types from ebpf_trace_types.go (newly supported in eBPF)
ptraceCmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(tmpDir, "__debug_ptrace"),
"--verbose", "3", fixturePath, "main.tracedSmallInts", "main.tracedSlice")
ptraceStderr, err := ptraceCmd.StderrPipe()
assertNoError(err, t, "ptrace stderr pipe")
defer ptraceStderr.Close()
assertNoError(ptraceCmd.Start(), t, "running ptrace trace")
ptraceOutput, err := io.ReadAll(ptraceStderr)
assertNoError(err, t, "reading ptrace output")
ptraceCmd.Wait()
if len(ptraceOutput) == 0 {
t.Fatal("ptrace backend produced no output")
}
// Run trace with eBPF backend at verbosity level 3
// Test small integer and slice types from ebpf_trace_types.go (newly supported in eBPF)
ebpfCmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(tmpDir, "__debug_ebpf"),
"--verbose", "3", fixturePath, "main.tracedSmallInts", "main.tracedSlice")
ebpfStderr, err := ebpfCmd.StderrPipe()
assertNoError(err, t, "ebpf stderr pipe")
defer ebpfStderr.Close()
assertNoError(ebpfCmd.Start(), t, "running ebpf trace")
ebpfOutput, err := io.ReadAll(ebpfStderr)
assertNoError(err, t, "reading ebpf output")
ebpfCmd.Wait()
if len(ebpfOutput) == 0 {
t.Fatal("ebpf backend produced no output")
}
// Filter out process exit messages
ptraceFiltered := filterProcessExitLines(ptraceOutput)
ebpfFiltered := filterProcessExitLines(ebpfOutput)
// Compare outputs byte-for-byte
if !bytes.Equal(ptraceFiltered, ebpfFiltered) {
t.Fatalf("Output mismatch between ptrace and ebpf backends at verbosity level 3:\n\nPtrace output:\n%s\n\neBPF output:\n%s",
string(ptraceOutput), string(ebpfOutput))
}
}
func TestDlvTestChdir(t *testing.T) {
t.Parallel()
dlvbin := protest.GetDlvBinary(t)

View File

@ -627,7 +627,7 @@ func (t *Target) setEBPFTracepointOnFunc(fn *Function, goidOffset int64) error {
var args []ebpf.UProbeArgMap
varEntries := reader.Variables(dwarfTree, fn.Entry, l, variablesFlags)
for _, entry := range varEntries {
_, dt, err := readVarEntry(entry.Tree, fn.cu.image)
name, dt, err := readVarEntry(entry.Tree, fn.cu.image)
if err != nil {
return err
}
@ -645,9 +645,11 @@ func (t *Target) setEBPFTracepointOnFunc(fn *Function, goidOffset int64) error {
isret, _ := entry.Val(dwarf.AttrVarParam).(bool)
offset += int64(t.BinInfo().Arch.PtrSize())
args = append(args, ebpf.UProbeArgMap{
Name: name,
Offset: offset,
Size: dt.Size(),
Kind: dt.Common().ReflectKind,
TypeName: dt.String(),
Pieces: paramPieces,
InReg: len(pieces) > 0,
Ret: isret,

View File

@ -8,15 +8,19 @@ import (
)
type UProbeArgMap struct {
Offset int64 // Offset from the stackpointer.
Size int64 // Size in bytes.
Kind reflect.Kind // Kind of variable.
Pieces []int // Pieces of the variables as stored in registers.
InReg bool // True if this param is contained in a register.
Ret bool // True if this param is a return value.
Name string // Parameter name from DWARF.
Offset int64 // Offset from the stackpointer.
Size int64 // Size in bytes.
Kind reflect.Kind // Kind of variable.
TypeName string // Original type name from DWARF (e.g., "*int", "[]byte").
Pieces []int // Pieces of the variables as stored in registers.
InReg bool // True if this param is contained in a register.
Ret bool // True if this param is a return value.
}
type RawUProbeParam struct {
Name string // Parameter name from DWARF.
TypeName string // Original type name from DWARF (e.g., "*int", "[]byte").
Pieces []op.Piece
RealType godwarf.Type
Kind reflect.Kind

View File

@ -66,6 +66,7 @@ type EBPFContext struct {
links []link.Link
parsedBpfEvents []RawUProbeParams
argTypeInfo map[uint64][]UProbeArgMap // Maps function address to argument type information
m sync.Mutex
ctx context.Context
@ -108,6 +109,15 @@ func (ctx *EBPFContext) UpdateArgMap(key uint64, goidOffset int64, args []UProbe
}
params := createFunctionParameterList(key, goidOffset, args, isret)
params.g_addr_offset = gAddrOffset
// Store argument type information for later use when parsing results
ctx.m.Lock()
if ctx.argTypeInfo == nil {
ctx.argTypeInfo = make(map[uint64][]UProbeArgMap)
}
ctx.argTypeInfo[key] = args
ctx.m.Unlock()
return ctx.bpfArgMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&params), ebpf.UpdateAny)
}
@ -175,7 +185,7 @@ func (ctx *EBPFContext) pollEvents() {
return
}
parsed := parseFunctionParameterList(e.RawSample)
parsed := parseFunctionParameterList(ctx, e.RawSample)
ctx.m.Lock()
ctx.parsedBpfEvents = append(ctx.parsedBpfEvents, parsed)
@ -184,7 +194,7 @@ func (ctx *EBPFContext) pollEvents() {
}
}
func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
func parseFunctionParameterList(ctx *EBPFContext, rawParamBytes []byte) RawUProbeParams {
params := (*function_parameter_list_t)(unsafe.Pointer(&rawParamBytes[0]))
defer runtime.KeepAlive(params) // Ensure the param is not garbage collected.
@ -194,12 +204,23 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
rawParams.GoroutineID = int(params.goroutine_id)
rawParams.IsRet = params.is_ret
parseParam := func(param function_parameter_t) *RawUProbeParam {
// Look up original type information for this function
ctx.m.Lock()
argTypes := ctx.argTypeInfo[params.fn_addr]
ctx.m.Unlock()
parseParam := func(param function_parameter_t, paramIdx int) *RawUProbeParam {
iparam := &RawUProbeParam{}
data := make([]byte, 0x60)
ret := param
iparam.Kind = reflect.Kind(ret.kind)
// Populate name and type name from argTypes if available
if paramIdx < len(argTypes) {
iparam.Name = argTypes[paramIdx].Name
iparam.TypeName = argTypes[paramIdx].TypeName
}
val := ret.val[:ret.size]
rawDerefValue := ret.deref_val[:0x30]
copy(data, val)
@ -215,19 +236,19 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
switch iparam.Kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size)}}}
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), Name: iparam.Kind.String(), ReflectKind: iparam.Kind}}}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
iparam.RealType = &godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size)}}}
iparam.RealType = &godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), Name: iparam.Kind.String(), ReflectKind: iparam.Kind}}}
case reflect.Bool:
iparam.RealType = &godwarf.BoolType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, ReflectKind: reflect.Bool}}}
iparam.RealType = &godwarf.BoolType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "bool", ReflectKind: reflect.Bool}}}
case reflect.Float32, reflect.Float64:
if !usesXMMRegisters(ret) {
iparam.RealType = &godwarf.FloatType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), ReflectKind: iparam.Kind}}}
iparam.RealType = &godwarf.FloatType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), Name: iparam.Kind.String(), ReflectKind: iparam.Kind}}}
}
// If in XMM registers, RealType stays nil, marked unreadable in target.go
case reflect.Complex64, reflect.Complex128:
if !usesXMMRegisters(ret) {
iparam.RealType = &godwarf.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), ReflectKind: iparam.Kind}}}
iparam.RealType = &godwarf.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), Name: iparam.Kind.String(), ReflectKind: iparam.Kind}}}
}
case reflect.Ptr, reflect.UnsafePointer:
// Display the raw pointer address as a uintptr value.
@ -238,14 +259,14 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
// 2) deref_val is limited to 48 bytes, insufficient for
// nested or variable-length pointed-to types
iparam.Kind = reflect.Uintptr
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), ReflectKind: reflect.Uintptr}}}
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(ret.size), Name: "uintptr", ReflectKind: reflect.Uintptr}}}
case reflect.Slice:
// Display the slice data pointer address as a uintptr value.
// Same limitations as pointers above: element type info is
// not available, and deref_val (48 bytes) can only hold a
// few elements.
iparam.Kind = reflect.Uintptr
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8, ReflectKind: reflect.Uintptr}}}
iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8, Name: "uintptr", ReflectKind: reflect.Uintptr}}}
case reflect.String:
strLen := binary.LittleEndian.Uint64(val[8:])
iparam.Base = FakeAddressBase + 0x30
@ -254,6 +275,7 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
StructType: godwarf.StructType{
CommonType: godwarf.CommonType{
ByteSize: 16,
Name: "string",
ReflectKind: reflect.String,
},
Kind: "struct",
@ -276,10 +298,11 @@ func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams {
}
for i := 0; i < int(params.n_parameters); i++ {
rawParams.InputParams = append(rawParams.InputParams, parseParam(params.params[i]))
rawParams.InputParams = append(rawParams.InputParams, parseParam(params.params[i], i))
}
// Return parameters start after input parameters in argTypes
for i := 0; i < int(params.n_ret_parameters); i++ {
rawParams.ReturnParams = append(rawParams.ReturnParams, parseParam(params.ret_params[i]))
rawParams.ReturnParams = append(rawParams.ReturnParams, parseParam(params.ret_params[i], int(params.n_parameters)+i))
}
return rawParams

View File

@ -496,6 +496,8 @@ func (t *Target) GetBufferedTracepoints() []*UProbeTraceResult {
tracepoints := t.proc.GetBufferedTracepoints()
convertInputParamToVariable := func(ip *ebpf.RawUProbeParam) *Variable {
v := &Variable{}
v.Name = ip.Name
v.DwarfType = ip.RealType
v.RealType = ip.RealType
v.Len = ip.Len
v.Base = ip.Base