terminal: add prompt when breakpoint is hit during next/step/stepout (#2548)

* terminal: add prompt when breakpoint is hit during next/step/stepout

Adds a prompt asking the user what to do when a breakpoint is hit on a
different goroutine while next/step/stepout is being executed.
Gives the user the opportunity to cancel next/step/stepout, continue
once skipping the breakpoint or automatically skipping all other
concurrent breakpoints until next/step/stepout is finished.

Fixes #2317
This commit is contained in:
Alessandro Arzilli 2021-07-26 17:57:09 +02:00 committed by GitHub
parent e9b20d5ee1
commit 2ecc025311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 42 deletions

@ -1446,8 +1446,28 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
}
return nil
}
skipBreakpoints := false
for {
fmt.Printf("\tbreakpoint hit during %s, continuing...\n", op)
fmt.Printf("\tbreakpoint hit during %s", op)
if !skipBreakpoints {
fmt.Printf("\n")
answer, err := promptAutoContinue(t, op)
switch answer {
case "f": // finish next
skipBreakpoints = true
fallthrough
case "c": // continue once
fmt.Printf("continuing...\n")
case "s": // stop and cancel
fallthrough
default:
t.client.CancelNext()
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
return err
}
} else {
fmt.Printf(", continuing...\n")
}
stateChan := t.client.DirectionCongruentContinue()
var state *api.DebuggerState
for state = range stateChan {
@ -1464,6 +1484,20 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
}
}
func promptAutoContinue(t *Term, op string) (string, error) {
for {
answer, err := t.line.Prompt(fmt.Sprintf("[c] continue [s] stop here and cancel %s, [f] finish %s skipping all breakpoints? ", op, op))
if err != nil {
return "", err
}
answer = strings.ToLower(strings.TrimSpace(answer))
switch answer {
case "f", "c", "s":
return answer, nil
}
}
}
func scopePrefixSwitch(t *Term, ctx callContext) error {
if ctx.Scope.GoroutineID > 0 {
_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)

@ -582,47 +582,6 @@ func countOccurrences(s, needle string) int {
return count
}
func TestIssue387(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("test is not valid on FreeBSD")
}
// a breakpoint triggering during a 'next' operation will interrupt it
test.AllowRecording(t)
withTestTerminal("issue387", t, func(term *FakeTerminal) {
breakpointHitCount := 0
term.MustExec("break dostuff")
for {
outstr, err := term.Exec("continue")
breakpointHitCount += countOccurrences(outstr, "issue387.go:8")
t.Log(outstr)
if err != nil {
if !strings.Contains(err.Error(), "exited") {
t.Fatalf("Unexpected error executing 'continue': %v", err)
}
break
}
pos := 9
for {
outstr = term.MustExec("next")
breakpointHitCount += countOccurrences(outstr, "issue387.go:8")
t.Log(outstr)
if countOccurrences(outstr, fmt.Sprintf("issue387.go:%d", pos)) == 0 {
t.Fatalf("did not continue to expected position %d", pos)
}
pos++
if pos >= 11 {
break
}
}
}
if breakpointHitCount != 10 {
t.Fatalf("Breakpoint hit wrong number of times, expected 10 got %d", breakpointHitCount)
}
})
}
func listIsAt(t *testing.T, term *FakeTerminal, listcmd string, cur, start, end int) {
t.Helper()
outstr := term.MustExec(listcmd)