From 29aa227c5c91bb01dcb75e12fcd0265b4e1f29c3 Mon Sep 17 00:00:00 2001 From: Archana Ravindar Date: Fri, 24 Apr 2026 00:22:31 +0530 Subject: [PATCH] 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 --- cmd/dlv/dlv_test.go | 371 +++++++++++++++++++++--------- pkg/proc/breakpoints.go | 4 +- pkg/proc/internal/ebpf/context.go | 16 +- pkg/proc/internal/ebpf/helpers.go | 47 +++- pkg/proc/target.go | 2 + 5 files changed, 314 insertions(+), 126 deletions(-) diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index 0aedb85d..6662c0ca 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -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 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) diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index 8228e3dd..ec783a79 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -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, diff --git a/pkg/proc/internal/ebpf/context.go b/pkg/proc/internal/ebpf/context.go index f6316f9e..dea7be8e 100644 --- a/pkg/proc/internal/ebpf/context.go +++ b/pkg/proc/internal/ebpf/context.go @@ -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 diff --git a/pkg/proc/internal/ebpf/helpers.go b/pkg/proc/internal/ebpf/helpers.go index 9a58f14d..38335c43 100644 --- a/pkg/proc/internal/ebpf/helpers.go +++ b/pkg/proc/internal/ebpf/helpers.go @@ -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(¶ms), 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 diff --git a/pkg/proc/target.go b/pkg/proc/target.go index 27c7c858..3434f015 100644 --- a/pkg/proc/target.go +++ b/pkg/proc/target.go @@ -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