proc: Step should skip function prologue

Step disassembles the current instruction, if it is a CALL sets a
temp breakpoint inside the called function, after the prologue and
calls Continue.

Fixes #332
This commit is contained in:
aarzilli 2016-02-12 08:43:22 +01:00 committed by Derek Parker
parent 4116d66c8d
commit 852f0c95b3
4 changed files with 33 additions and 8 deletions

@ -6,6 +6,8 @@ import (
"rsc.io/x86/x86asm"
)
var maxInstructionLength uint64 = 15
type ArchInst x86asm.Inst
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {

@ -452,6 +452,15 @@ func (dbp *Process) Step() (err error) {
return err
}
for {
pc, err := dbp.CurrentThread.PC()
if err != nil {
return err
}
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)
}
err = dbp.CurrentThread.StepInstruction()
if err != nil {
return err
@ -471,6 +480,15 @@ func (dbp *Process) Step() (err error) {
return dbp.run(fn)
}
// StepInto sets a temp breakpoint after the prologue of fn and calls Continue
func (dbp *Process) StepInto(fn *gosym.Func) error {
pc, _ := dbp.FirstPCAfterPrologue(fn, false)
if _, err := dbp.SetTempBreakpoint(pc); err != nil {
return err
}
return dbp.Continue()
}
// StepInstruction will continue the current thread for exactly
// one instruction. This method affects only the thread
// asssociated with the selected goroutine. All other

@ -19,8 +19,6 @@ import (
protest "github.com/derekparker/delve/proc/test"
)
const disableFailingTests = true
func init() {
runtime.GOMAXPROCS(4)
os.Setenv("GOMAXPROCS", "4")
@ -1463,7 +1461,6 @@ func TestStepIntoFunction(t *testing.T) {
if !strings.Contains(loc.File, "teststep") {
t.Fatalf("debugger stopped at incorrect location: %s:%d", loc.File, loc.Line)
}
// TODO(derekparker) consider skipping function prologue when stepping into func.
if loc.Line != 8 {
t.Fatalf("debugger stopped at incorrect line: %d", loc.Line)
}
@ -1511,10 +1508,6 @@ func TestIssue332_Part2(t *testing.T) {
// In some parts of the prologue, for some functions, the FDE data is incorrect
// which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>"
// because the incorrect FDE data leads to reading the wrong stack address as the return address
// TODO: the incorrect FDE data problem is fixed in go 1.6, reenable this test when 1.6 is released.
if disableFailingTests {
return
}
withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 8)
assertNoError(err, t, "LineToPC()")
@ -1535,6 +1528,18 @@ func TestIssue332_Part2(t *testing.T) {
}
}
pc, err := p.CurrentThread.PC()
assertNoError(err, t, "PC()")
pcAfterPrologue, err := p.FindFunctionLocation("main.changeMe", true, -1)
assertNoError(err, t, "FindFunctionLocation()")
pcEntry, err := p.FindFunctionLocation("main.changeMe", false, 0)
if pcAfterPrologue == pcEntry {
t.Fatalf("main.changeMe and main.changeMe:0 are the same (%x)", pcAfterPrologue)
}
if pc != pcAfterPrologue {
t.Fatalf("Step did not skip the prologue: current pc: %x, first instruction after prologue: %x", pc, pcAfterPrologue)
}
assertNoError(p.Next(), t, "first Next()")
assertNoError(p.Next(), t, "second Next()")
assertNoError(p.Next(), t, "third Next()")

@ -5,9 +5,9 @@ import "C"
import (
"bytes"
"fmt"
"rsc.io/x86/x86asm"
"syscall"
"unsafe"
"rsc.io/x86/x86asm"
)
// Regs represents CPU registers on an AMD64 processor.