From 788df884e6920167df075ab7a9e06886a5dd5ee9 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Mon, 16 Oct 2023 17:57:33 +0200 Subject: [PATCH] proc: use DW_AT_trampoline to detect auto-generated code (#3528) Use the trampoline attribute to detect auto-generated code. This fixes a bug where stepping into a method of a generic type called through an interface will take the debugger into an auto-generated wrapper that does not have a dictionary and using next will step out of the wrapper. Fixes a bug reported on the #delve channel of the gophers slack server. --- _fixtures/genericintoiface.go | 25 +++++++++++++++++++++ pkg/proc/proc_test.go | 41 +++++++++++++++++++++++++++++++++++ pkg/proc/target_exec.go | 2 +- pkg/proc/variables.go | 2 +- 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 _fixtures/genericintoiface.go diff --git a/_fixtures/genericintoiface.go b/_fixtures/genericintoiface.go new file mode 100644 index 00000000..fb6dca78 --- /dev/null +++ b/_fixtures/genericintoiface.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +type Blah[T any] struct { + x T +} + +func (b *Blah[T]) F(y T) { + b.x = y +} + +type BlahInt interface { + F(int) +} + +func callf(b BlahInt) { + b.F(2) + fmt.Println(b) +} + +func main() { + b := &Blah[int]{10} + callf(b) +} diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 2440a797..714a4d1d 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -428,6 +428,7 @@ const ( contReverseStep contReverseStepout contContinueToBreakpoint + contNothing ) type seqTest struct { @@ -531,6 +532,8 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te assertNoError(grp.Continue(), t, "Continue() returned an error") err := p.ClearBreakpoint(bp.Addr) assertNoError(err, t, "ClearBreakpoint() returned an error") + case contNothing: + // do nothing } f, ln = currentLineNumber(p, t) @@ -552,6 +555,10 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te if !strings.HasSuffix(f, v[0]) || (ln != tgtln) { t.Fatalf("Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i) } + case func(*proc.Target): + pos(p) + default: + panic(fmt.Errorf("unexpected type %T", pos)) } } @@ -6262,3 +6269,37 @@ func TestWaitForAttach(t *testing.T) { cmd.Wait() } + +func TestNextGenericMethodThroughInterface(t *testing.T) { + // Tests that autogenerated wrappers for generic methods called through an + // interface are skipped. + + varcheck := func(p *proc.Target) { + yvar := evalVariable(p, t, "y") + yval, _ := constant.Int64Val(yvar.Value) + if yval != 2 { + t.Errorf("expected 2 got %#v", yvar.Value) + } + } + + if runtime.GOOS == "linux" && runtime.GOARCH == "386" { + testseq2(t, "genericintoiface", "main.callf", []seqTest{ + {contContinue, 17}, + {contStep, 18}, + {contStep, 10}, + {contNothing, varcheck}, + {contNext, 11}, + {contNext, 19}, + }) + } else { + testseq2(t, "genericintoiface", "main.callf", []seqTest{ + {contContinue, 17}, + {contStep, 18}, + {contStep, 9}, + {contNext, 10}, + {contNothing, varcheck}, + {contNext, 11}, + {contNext, 19}, + }) + } +} diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go index be677c4c..77edae83 100644 --- a/pkg/proc/target_exec.go +++ b/pkg/proc/target_exec.go @@ -990,7 +990,7 @@ func allowDuplicateBreakpoint(bp *Breakpoint, err error) (*Breakpoint, error) { } func isAutogenerated(loc Location) bool { - return loc.File == "" && loc.Line == 1 + return (loc.File == "" && loc.Line == 1) || (loc.Fn != nil && loc.Fn.trampoline) } func isAutogeneratedOrDeferReturn(loc Location) bool { diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 60c13f96..fd18990d 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -1191,7 +1191,7 @@ func extractVarInfoFromEntry(tgt *Target, bi *BinaryInfo, image *Image, regs op. t, err = resolveParametricType(bi, mem, t, dictAddr) if err != nil { // Log the error, keep going with t, which will be the shape type - logflags.DebuggerLogger().Errorf("could not resolve parametric type of %s", n) + logflags.DebuggerLogger().Errorf("could not resolve parametric type of %s: %v", n, err) } addr, pieces, descr, err := bi.Location(entry, dwarf.AttrLocation, regs.PC(), regs, mem)