proc, terminal: step on parked goroutines
Previously Step would step through the goroutine associated with CurrentThread if SelectedGoroutine was parked
This commit is contained in:
parent
d17c7c3a61
commit
6faa78ff7c
@ -424,6 +424,10 @@ func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func (dbp *Process) Step() (err error) {
|
||||
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.thread == nil {
|
||||
// Step called on parked goroutine
|
||||
return dbp.stepToPC(dbp.SelectedGoroutine.PC)
|
||||
}
|
||||
fn := func() error {
|
||||
var nloc *Location
|
||||
th := dbp.CurrentThread
|
||||
@ -468,6 +472,10 @@ func (dbp *Process) StepInto(fn *gosym.Func) error {
|
||||
}
|
||||
}
|
||||
pc, _ := dbp.FirstPCAfterPrologue(fn, false)
|
||||
return dbp.stepToPC(pc)
|
||||
}
|
||||
|
||||
func (dbp *Process) stepToPC(pc uint64) error {
|
||||
if _, err := dbp.SetTempBreakpoint(pc, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1789,3 +1789,40 @@ func TestNextParked(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStepParked(t *testing.T) {
|
||||
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
|
||||
bp, err := setFunctionBreakpoint(p, "main.sayhi")
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
|
||||
// continue until a parked goroutine exists
|
||||
var parkedg *G
|
||||
LookForParkedG:
|
||||
for {
|
||||
err := p.Continue()
|
||||
if _, exited := err.(ProcessExitedError); exited {
|
||||
t.Log("could not find parked goroutine")
|
||||
return
|
||||
}
|
||||
assertNoError(err, t, "Continue()")
|
||||
|
||||
gs, err := p.GoroutinesInfo()
|
||||
assertNoError(err, t, "GoroutinesInfo()")
|
||||
|
||||
for _, g := range gs {
|
||||
if g.thread == nil {
|
||||
parkedg = g
|
||||
break LookForParkedG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()")
|
||||
p.ClearBreakpoint(bp.Addr)
|
||||
assertNoError(p.Step(), t, "Step()")
|
||||
|
||||
if p.SelectedGoroutine.ID != parkedg.ID {
|
||||
t.Fatalf("Step did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.SelectedGoroutine.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -124,37 +124,47 @@ func (tbe ThreadBlockedError) Error() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Set breakpoints for potential next lines.
|
||||
func (dbp *Process) setNextBreakpoints() (err error) {
|
||||
// returns topmost frame of g or thread if g is nil
|
||||
func topframe(g *G, thread *Thread) (Stackframe, error) {
|
||||
var frames []Stackframe
|
||||
var err error
|
||||
|
||||
if dbp.SelectedGoroutine == nil {
|
||||
if dbp.CurrentThread.blocked() {
|
||||
return ThreadBlockedError{}
|
||||
if g == nil {
|
||||
if thread.blocked() {
|
||||
return Stackframe{}, ThreadBlockedError{}
|
||||
}
|
||||
frames, err = dbp.CurrentThread.Stacktrace(0)
|
||||
frames, err = thread.Stacktrace(0)
|
||||
} else {
|
||||
frames, err = dbp.SelectedGoroutine.Stacktrace(0)
|
||||
frames, err = g.Stacktrace(0)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return Stackframe{}, err
|
||||
}
|
||||
if len(frames) < 1 {
|
||||
return errors.New("empty stack trace")
|
||||
return Stackframe{}, errors.New("empty stack trace")
|
||||
}
|
||||
return frames[0], nil
|
||||
}
|
||||
|
||||
// Set breakpoints for potential next lines.
|
||||
func (dbp *Process) setNextBreakpoints() (err error) {
|
||||
topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Grab info on our current stack frame. Used to determine
|
||||
// whether we may be stepping outside of the current function.
|
||||
fde, err := dbp.frameEntries.FDEForPC(frames[0].Current.PC)
|
||||
fde, err := dbp.frameEntries.FDEForPC(topframe.Current.PC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if filepath.Ext(frames[0].Current.File) != ".go" {
|
||||
return dbp.cnext(frames[0], fde)
|
||||
if filepath.Ext(topframe.Current.File) != ".go" {
|
||||
return dbp.cnext(topframe, fde)
|
||||
}
|
||||
|
||||
return dbp.next(dbp.SelectedGoroutine, frames[0], fde)
|
||||
return dbp.next(dbp.SelectedGoroutine, topframe, fde)
|
||||
}
|
||||
|
||||
// Set breakpoints at every line, and the return address. Also look for
|
||||
@ -212,6 +222,7 @@ func (dbp *Process) setTempBreakpoints(curpc uint64, pcs []uint64, cond ast.Expr
|
||||
}
|
||||
if _, err := dbp.SetTempBreakpoint(pcs[i], cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearTempBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ A tracepoint is a breakpoint that does not stop the execution of the program, in
|
||||
See also: "help on", "help cond" and "help clear"`},
|
||||
{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: "Restart process."},
|
||||
{aliases: []string{"continue", "c"}, cmdFn: cont, helpMsg: "Run until breakpoint or program termination."},
|
||||
{aliases: []string{"step", "s"}, cmdFn: step, helpMsg: "Single step through program."},
|
||||
{aliases: []string{"step", "s"}, allowedPrefixes: scopePrefix, cmdFn: step, helpMsg: "Single step through program."},
|
||||
{aliases: []string{"step-instruction", "si"}, cmdFn: stepInstruction, helpMsg: "Single step a single cpu instruction."},
|
||||
{aliases: []string{"next", "n"}, allowedPrefixes: scopePrefix, cmdFn: next, helpMsg: "Step over to next source line."},
|
||||
{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
|
||||
@ -605,7 +605,26 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string) err
|
||||
}
|
||||
}
|
||||
|
||||
func scopePrefixSwitch(t *Term, ctx callContext) error {
|
||||
if ctx.Prefix != scopePrefix {
|
||||
return nil
|
||||
}
|
||||
if ctx.Scope.Frame != 0 {
|
||||
return errors.New("frame prefix not accepted")
|
||||
}
|
||||
if ctx.Scope.GoroutineID > 0 {
|
||||
_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func step(t *Term, ctx callContext, args string) error {
|
||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := t.client.Step()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -625,16 +644,8 @@ func stepInstruction(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
|
||||
func next(t *Term, ctx callContext, args string) error {
|
||||
if ctx.Prefix == scopePrefix {
|
||||
if ctx.Scope.Frame != 0 {
|
||||
return errors.New("can not prefix next with frame")
|
||||
}
|
||||
if ctx.Scope.GoroutineID > 0 {
|
||||
_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := t.client.Next()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user