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.
This commit is contained in:
parent
04bb7fda33
commit
788df884e6
25
_fixtures/genericintoiface.go
Normal file
25
_fixtures/genericintoiface.go
Normal file
@ -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)
|
||||||
|
}
|
@ -428,6 +428,7 @@ const (
|
|||||||
contReverseStep
|
contReverseStep
|
||||||
contReverseStepout
|
contReverseStepout
|
||||||
contContinueToBreakpoint
|
contContinueToBreakpoint
|
||||||
|
contNothing
|
||||||
)
|
)
|
||||||
|
|
||||||
type seqTest struct {
|
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")
|
assertNoError(grp.Continue(), t, "Continue() returned an error")
|
||||||
err := p.ClearBreakpoint(bp.Addr)
|
err := p.ClearBreakpoint(bp.Addr)
|
||||||
assertNoError(err, t, "ClearBreakpoint() returned an error")
|
assertNoError(err, t, "ClearBreakpoint() returned an error")
|
||||||
|
case contNothing:
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
f, ln = currentLineNumber(p, t)
|
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) {
|
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)
|
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()
|
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},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -990,7 +990,7 @@ func allowDuplicateBreakpoint(bp *Breakpoint, err error) (*Breakpoint, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isAutogenerated(loc Location) bool {
|
func isAutogenerated(loc Location) bool {
|
||||||
return loc.File == "<autogenerated>" && loc.Line == 1
|
return (loc.File == "<autogenerated>" && loc.Line == 1) || (loc.Fn != nil && loc.Fn.trampoline)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAutogeneratedOrDeferReturn(loc Location) bool {
|
func isAutogeneratedOrDeferReturn(loc Location) bool {
|
||||||
|
@ -1191,7 +1191,7 @@ func extractVarInfoFromEntry(tgt *Target, bi *BinaryInfo, image *Image, regs op.
|
|||||||
t, err = resolveParametricType(bi, mem, t, dictAddr)
|
t, err = resolveParametricType(bi, mem, t, dictAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log the error, keep going with t, which will be the shape type
|
// 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)
|
addr, pieces, descr, err := bi.Location(entry, dwarf.AttrLocation, regs.PC(), regs, mem)
|
||||||
|
Loading…
Reference in New Issue
Block a user