diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index f6d920b14c524e9bb9e1a26608d6db4935ba3e7a..b351f8fc387e845bbfe28a467094e04ab609972b 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 4ce45a08e61c4187acc6b350f11689253ce25612..9c319551c28abcc294012afa6707cb19ead70792 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 fcfa6ee5c2e52dcfe08d42dcd5587fb946253954..66dcf2532b8308638328da6d3f24ea35c1b1f327 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") + }) +}