proc: Fix stepping into runtime.duff* (#575)

Detect calls that do not target a function's entrypoint
(i.e, calls to runtime.duffzero and runtime.duffcopy) and
instead step into them directly.  StepInto sets a breakpoint
past the called function's prologue and expects that continue
will hit that breakpoint, but because the call is into the
interior of the function (well past the prologue) this fails.

Fixes #573
This commit is contained in:
dr2chase 2016-07-21 16:10:35 -04:00 committed by Derek Parker
parent 16f16cf86d
commit 218c2b953b
3 changed files with 49 additions and 1 deletions

28
_fixtures/issue573.go Normal file

@ -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)
}

@ -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()

@ -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.
})
}