From 5c2673a6322ad37e2bb0a42167f912ccd3022fdc Mon Sep 17 00:00:00 2001 From: aarzilli Date: Fri, 23 Jun 2017 14:22:26 +0200 Subject: [PATCH] proc/native, proc/gdbserial: StepInstruction without goroutine proc.Process.StepInstruction should work even if there is no goroutine selected. --- pkg/proc/gdbserial/gdbserver.go | 21 +++++++++++---------- pkg/proc/native/proc.go | 24 ++++++++++++------------ pkg/proc/proc_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index f6d920b1..b351f8fc 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -623,25 +623,26 @@ continueLoop: } func (p *Process) StepInstruction() error { - if p.selectedGoroutine == nil { - return errors.New("cannot single step: no selected goroutine") - } - if p.selectedGoroutine.Thread == nil { - if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil { - return err + thread := p.currentThread + if p.selectedGoroutine != nil { + if p.selectedGoroutine.Thread == nil { + if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil { + return err + } + return proc.Continue(p) } - return proc.Continue(p) + thread = p.selectedGoroutine.Thread.(*Thread) } p.allGCache = nil if p.exited { return &proc.ProcessExitedError{Pid: p.conn.pid} } - p.selectedGoroutine.Thread.(*Thread).clearBreakpointState() - err := p.selectedGoroutine.Thread.(*Thread).StepInstruction() + thread.clearBreakpointState() + err := thread.StepInstruction() if err != nil { return err } - return p.selectedGoroutine.Thread.(*Thread).SetCurrentBreakpoint() + return thread.SetCurrentBreakpoint() } func (p *Process) SwitchThread(tid int) error { diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go index 4ce45a08..9c319551 100644 --- a/pkg/proc/native/proc.go +++ b/pkg/proc/native/proc.go @@ -1,7 +1,6 @@ package native import ( - "errors" "fmt" "go/ast" "os" @@ -297,26 +296,27 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) { // asssociated with the selected goroutine. All other // threads will remain stopped. func (dbp *Process) StepInstruction() (err error) { - if dbp.selectedGoroutine == nil { - return errors.New("cannot single step: no selected goroutine") - } - if dbp.selectedGoroutine.Thread == nil { - // Step called on parked goroutine - if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil { - return err + thread := dbp.currentThread + if dbp.selectedGoroutine != nil { + if dbp.selectedGoroutine.Thread == nil { + // Step called on parked goroutine + if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil { + return err + } + return proc.Continue(dbp) } - return proc.Continue(dbp) + thread = dbp.selectedGoroutine.Thread.(*Thread) } dbp.allGCache = nil if dbp.exited { return &proc.ProcessExitedError{} } - dbp.selectedGoroutine.Thread.(*Thread).clearBreakpointState() - err = dbp.selectedGoroutine.Thread.(*Thread).StepInstruction() + thread.clearBreakpointState() + err = thread.StepInstruction() if err != nil { return err } - return dbp.selectedGoroutine.Thread.(*Thread).SetCurrentBreakpoint() + return thread.SetCurrentBreakpoint() } // SwitchThread changes from current thread to the thread specified by `tid`. diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index fcfa6ee5..66dcf253 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/derekparker/delve/pkg/dwarf/frame" "github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc/gdbserial" "github.com/derekparker/delve/pkg/proc/native" @@ -2954,3 +2955,29 @@ func TestIssue877(t *testing.T) { } }) } + +func TestIssue893(t *testing.T) { + // Test what happens when next is called immediately after launching the + // executable, acceptable behaviors are: (a) no error, (b) no source at PC + // error. + protest.AllowRecording(t) + withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + err := proc.Next(p) + if err == nil { + return + } + if _, ok := err.(*frame.NoFDEForPCError); ok { + return + } + assertNoError(err, t, "Next") + }) +} + +func TestStepInstructionNoGoroutine(t *testing.T) { + protest.AllowRecording(t) + withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + // Call StepInstruction immediately after launching the program, it should + // work even though no goroutine is selected. + assertNoError(p.StepInstruction(), t, "StepInstruction") + }) +} -- GitLab