diff --git a/_fixtures/issue573.go b/_fixtures/issue573.go new file mode 100644 index 0000000000000000000000000000000000000000..15b33aea20c17c680f7d6bcba52788d21f937e23 --- /dev/null +++ b/_fixtures/issue573.go @@ -0,0 +1,28 @@ +package main + +// A debugger test. +// dlv debug +// b main.foo +// c +// s +// s +// Expect to be stopped in fmt.Printf or runtime.duffzero +// In bug, s #2 runs to the process exit because the call +// to duffzero enters duffzero well after the nominal entry +// and skips the temporary breakpoint placed by StepZero(). +import "fmt" + +var v int = 99 + +func foo(x, y int) (z int) { // c stops here + fmt.Printf("x=%d, y=%d, z=%d\n", x, y, z) // s #1 stops here; s #2 is supposed to stop in Printf or duffzero. + z = x + y + return +} + +func main() { + x := v + y := x * x + z := foo(x, y) + fmt.Printf("z=%d\n", z) +} diff --git a/proc/proc.go b/proc/proc.go index 15bacf7f22bce0d1f186d92b9266425f545911ea..8b623284c5ecc2561bbb6328c87a1d9c6723b6c2 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -406,7 +406,13 @@ func (dbp *Process) Step() (err error) { } text, err := dbp.CurrentThread.Disassemble(pc, pc+maxInstructionLength, true) if err == nil && len(text) > 0 && text[0].IsCall() && text[0].DestLoc != nil && text[0].DestLoc.Fn != nil { - return dbp.StepInto(text[0].DestLoc.Fn) + fn := text[0].DestLoc.Fn + // Ensure PC and Entry match, otherwise StepInto is likely to set + // its breakpoint before DestLoc.PC and hence run too far ahead. + // Calls to runtime.duffzero and duffcopy have this problem. + if fn.Entry == text[0].DestLoc.PC { + return dbp.StepInto(fn) + } } err = dbp.CurrentThread.StepInstruction() diff --git a/proc/proc_test.go b/proc/proc_test.go index 3c98d0261c55b96d42bd53711f04cc163a5f1872..755e3f0396f49fa029b386cca8457a35075241c2 100644 --- a/proc/proc_test.go +++ b/proc/proc_test.go @@ -1881,3 +1881,17 @@ func TestUnsupportedArch(t *testing.T) { t.Fatal(err) } } + +func Test1Issue573(t *testing.T) { + // calls to runtime.duffzero and runtime.duffcopy jump directly into the middle + // of the function and the temp breakpoint set by StepInto may be missed. + withTestProcess("issue573", t, func(p *Process, fixture protest.Fixture) { + f := p.goSymTable.LookupFunc("main.foo") + _, err := p.SetBreakpoint(f.Entry) + assertNoError(err, t, "SetBreakpoint()") + assertNoError(p.Continue(), t, "Continue()") + assertNoError(p.Step(), t, "Step() #1") + assertNoError(p.Step(), t, "Step() #2") // Bug exits here. + assertNoError(p.Step(), t, "Step() #3") // Third step ought to be possible; program ought not have exited. + }) +}