terminal: make printcontext use SelectedGoroutine
printcontext should use SelectedGoroutine instead of trusting that the goroutine running on current thread matches the SelectedGoroutine. When the user switches to a parked goroutine CurrentThread and SelectedGoroutine will diverge. Almost all calls to printcontext are safe, they happen after a continue command returns when SelectedGoroutine and CurrentThread always agree, but the calls in frameCommand and listCommand are wrong. Additionally we should stop reporting an error when the debugger is stopped on an unknown PC address.
This commit is contained in:
parent
4e177bb99a
commit
4f70ff0a77
@ -128,6 +128,9 @@ func Continue(dbp Process) error {
|
||||
}
|
||||
loc, err := curthread.Location()
|
||||
if err != nil || loc.Fn == nil || (loc.Fn.Name != "runtime.breakpoint" && loc.Fn.Name != "runtime.Breakpoint") {
|
||||
if g := dbp.SelectedGoroutine(); g != nil {
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -73,10 +73,10 @@ func (c command) match(cmdstr string) bool {
|
||||
|
||||
// Commands represents the commands for Delve terminal process.
|
||||
type Commands struct {
|
||||
cmds []command
|
||||
lastCmd cmdfunc
|
||||
client service.Client
|
||||
frame int // Current frame as set by frame/up/down commands.
|
||||
cmds []command
|
||||
lastCmd cmdfunc
|
||||
client service.Client
|
||||
frame int // Current frame as set by frame/up/down commands.
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1302,6 +1302,9 @@ func listCommand(t *Term, ctx callContext, args string) error {
|
||||
return err
|
||||
}
|
||||
printcontext(t, state)
|
||||
if state.SelectedGoroutine != nil {
|
||||
return printfile(t, state.SelectedGoroutine.CurrentLoc.File, state.SelectedGoroutine.CurrentLoc.Line, true)
|
||||
}
|
||||
return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
||||
|
||||
case len(args) == 0 && ctx.scoped():
|
||||
@ -1462,13 +1465,30 @@ func printcontext(t *Term, state *api.DebuggerState) error {
|
||||
fmt.Println("No current thread available")
|
||||
return nil
|
||||
}
|
||||
if len(state.CurrentThread.File) == 0 {
|
||||
|
||||
var th *api.Thread
|
||||
if state.SelectedGoroutine == nil {
|
||||
th = state.CurrentThread
|
||||
} else {
|
||||
for i := range state.Threads {
|
||||
if state.Threads[i].ID == state.SelectedGoroutine.ThreadID {
|
||||
th = state.Threads[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if th == nil {
|
||||
printcontextLocation(state.SelectedGoroutine.CurrentLoc)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if th.File == "" {
|
||||
fmt.Printf("Stopped at: 0x%x\n", state.CurrentThread.PC)
|
||||
t.Println("=>", "no source available")
|
||||
return nil
|
||||
}
|
||||
|
||||
printcontextThread(t, state.CurrentThread)
|
||||
printcontextThread(t, th)
|
||||
|
||||
if state.When != "" {
|
||||
fmt.Println(state.When)
|
||||
@ -1477,14 +1497,19 @@ func printcontext(t *Term, state *api.DebuggerState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func printcontextLocation(loc api.Location) {
|
||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name, ShortenFilePath(loc.File), loc.Line, loc.PC)
|
||||
if loc.Function != nil && loc.Function.Optimized {
|
||||
fmt.Println(optimizedFunctionWarning)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func printcontextThread(t *Term, th *api.Thread) {
|
||||
fn := th.Function
|
||||
|
||||
if th.Breakpoint == nil {
|
||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", fn.Name, ShortenFilePath(th.File), th.Line, th.PC)
|
||||
if th.Function != nil && th.Function.Optimized {
|
||||
fmt.Println(optimizedFunctionWarning)
|
||||
}
|
||||
printcontextLocation(api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
|
||||
return
|
||||
}
|
||||
|
||||
@ -1561,6 +1586,9 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
}
|
||||
|
||||
func printfile(t *Term, filename string, line int, showArrow bool) error {
|
||||
if filename == "" {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open(t.substitutePath(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -748,3 +748,38 @@ func TestIssue1090(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrintContextParkedGoroutine(t *testing.T) {
|
||||
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
|
||||
term.MustExec("break stacktraceme")
|
||||
term.MustExec("continue")
|
||||
|
||||
// pick a goroutine that isn't running on a thread
|
||||
gid := ""
|
||||
gout := strings.Split(term.MustExec("goroutines"), "\n")
|
||||
t.Logf("goroutines -> %q", gout)
|
||||
for _, gline := range gout {
|
||||
if !strings.Contains(gline, "thread ") && strings.Contains(gline, "agoroutine") {
|
||||
if dash := strings.Index(gline, " - "); dash > 0 {
|
||||
gid = gline[len(" Goroutine "):dash]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("picked %q", gid)
|
||||
term.MustExec(fmt.Sprintf("goroutine %s", gid))
|
||||
|
||||
frameout := strings.Split(term.MustExec("frame 0"), "\n")
|
||||
t.Logf("frame 0 -> %q", frameout)
|
||||
if strings.Contains(frameout[0], "stacktraceme") {
|
||||
t.Fatal("bad output for `frame 0` command on a parked goorutine")
|
||||
}
|
||||
|
||||
listout := strings.Split(term.MustExec("list"), "\n")
|
||||
t.Logf("list -> %q", listout)
|
||||
if strings.Contains(listout[0], "stacktraceme") {
|
||||
t.Fatal("bad output for list command on a parked goroutine")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user