diff --git a/cmd/dlv/main.go b/cmd/dlv/main.go index 8c7fc798f30f2aabdeac8d7f935b034ee3e7a5cd..e64c166afd37a8be3334e406e554ac47bc6cc2e3 100644 --- a/cmd/dlv/main.go +++ b/cmd/dlv/main.go @@ -193,18 +193,23 @@ starts and attaches to it, and enables you to immediately begin debugging your p fmt.Fprintln(os.Stderr, state.Err) return 0 } - var args []string - var fname string - if state.CurrentThread != nil && state.CurrentThread.Function != nil { - fname = state.CurrentThread.Function.Name - } - if state.BreakpointInfo != nil { - for _, arg := range state.BreakpointInfo.Arguments { - args = append(args, arg.SinglelineString()) + for i := range state.Threads { + th := state.Threads[i] + if th.Breakpoint == nil { + continue + } + var args []string + var fname string + if th.Function != nil { + fname = th.Function.Name + } + if th.BreakpointInfo != nil { + for _, arg := range th.BreakpointInfo.Arguments { + args = append(args, arg.SinglelineString()) + } } + fmt.Printf("%s(%s) %s:%d\n", fname, strings.Join(args, ", "), terminal.ShortenFilePath(th.File), th.Line) } - fp := terminal.ShortenFilePath(state.CurrentThread.File) - fmt.Printf("%s(%s) %s:%d\n", fname, strings.Join(args, ", "), fp, state.CurrentThread.Line) case <-sigChan: server.Stop(traceAttachPid == 0) return 1 diff --git a/service/api/conversions.go b/service/api/conversions.go index 1bf9ff162e0fd78022f46196dd0a17e3e4acfccd..064d0b90eb53667c9b1f904fe58ba4c2fbb9de42 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -37,6 +37,7 @@ func ConvertThread(th *proc.Thread) *Thread { file string line int pc uint64 + gid int ) loc, err := th.Location() @@ -47,12 +48,24 @@ func ConvertThread(th *proc.Thread) *Thread { function = ConvertFunction(loc.Fn) } + var bp *Breakpoint + + if th.CurrentBreakpoint != nil { + bp = ConvertBreakpoint(th.CurrentBreakpoint) + } + + if g, _ := th.GetG(); g != nil { + gid = g.Id + } + return &Thread{ - ID: th.Id, - PC: pc, - File: file, - Line: line, - Function: function, + ID: th.Id, + PC: pc, + File: file, + Line: line, + Function: function, + GoroutineID: gid, + Breakpoint: bp, } } diff --git a/service/api/types.go b/service/api/types.go index 12c635df333e95fddf974307c12d1991035562e8..62cab6e16e23456befef901ca36e8d640f9144ec 100644 --- a/service/api/types.go +++ b/service/api/types.go @@ -4,19 +4,15 @@ import "reflect" // DebuggerState represents the current context of the debugger. type DebuggerState struct { - // Breakpoint is the current breakpoint at which the debugged process is - // suspended, and may be empty if the process is not suspended. - Breakpoint *Breakpoint `json:"breakPoint,omitempty"` // CurrentThread is the currently selected debugger thread. CurrentThread *Thread `json:"currentThread,omitempty"` // SelectedGoroutine is the currently selected goroutine SelectedGoroutine *Goroutine `json:"currentGoroutine,omitempty"` - // Information requested by the current breakpoint - BreakpointInfo *BreakpointInfo `json:"breakPointInfo,omitrempty"` + // List of all the process threads + Threads []*Thread // Exited indicates whether the debugged process has exited. Exited bool `json:"exited"` ExitStatus int `json:"exitStatus"` - // Filled by RPCClient.Continue, indicates an error Err error `json:"-"` } @@ -62,6 +58,14 @@ type Thread struct { Line int `json:"line"` // Function is function information at the program counter. May be nil. Function *Function `json:"function,omitempty"` + + // ID of the goroutine running on this thread + GoroutineID int `json:"goroutineID"` + + // Breakpoint this thread is stopped at + Breakpoint *Breakpoint `json:"breakPoint,omitempty"` + // Informations requested by the current breakpoint + BreakpointInfo *BreakpointInfo `json:"breakPointInfo,omitrempty"` } type Location struct { diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 03c835cd8ced9531983b77db6cb510d7fd117b4a..50c88e1c85a6ba1d30284d16981e862a5dd6c1ee 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -111,31 +111,26 @@ func (d *Debugger) State() (*api.DebuggerState, error) { var ( state *api.DebuggerState - thread *api.Thread goroutine *api.Goroutine ) - if d.process.CurrentThread != nil { - thread = api.ConvertThread(d.process.CurrentThread) - } - if d.process.SelectedGoroutine != nil { goroutine = api.ConvertGoroutine(d.process.SelectedGoroutine) } - var breakpoint *api.Breakpoint - bp := d.process.CurrentBreakpoint() - if bp != nil { - breakpoint = api.ConvertBreakpoint(bp) - } - state = &api.DebuggerState{ - Breakpoint: breakpoint, - CurrentThread: thread, SelectedGoroutine: goroutine, Exited: d.process.Exited(), } + for i := range d.process.Threads { + th := api.ConvertThread(d.process.Threads[i]) + state.Threads = append(state.Threads, th) + if i == d.process.CurrentThread.Id { + state.CurrentThread = th + } + } + return state, nil } @@ -272,52 +267,59 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er } func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error { - if state == nil || state.Breakpoint == nil { + if state == nil { return nil } - bp := state.Breakpoint - bpi := &api.BreakpointInfo{} - state.BreakpointInfo = bpi + for i := range state.Threads { + if state.Threads[i].Breakpoint == nil { + continue + } + + bp := state.Threads[i].Breakpoint + bpi := &api.BreakpointInfo{} + state.Threads[i].BreakpointInfo = bpi - if bp.Goroutine { - g, err := d.process.CurrentThread.GetG() - if err != nil { - return err + if bp.Goroutine { + g, err := d.process.CurrentThread.GetG() + if err != nil { + return err + } + bpi.Goroutine = api.ConvertGoroutine(g) } - bpi.Goroutine = api.ConvertGoroutine(g) - } - if bp.Stacktrace > 0 { - rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace) - if err != nil { - return err + if bp.Stacktrace > 0 { + rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace) + if err != nil { + return err + } + bpi.Stacktrace, err = d.convertStacktrace(rawlocs, false) + if err != nil { + return err + } } - bpi.Stacktrace, err = d.convertStacktrace(rawlocs, false) + + s, err := d.process.CurrentThread.Scope() if err != nil { return err } - } - - s, err := d.process.CurrentThread.Scope() - if err != nil { - return err - } - if len(bp.Variables) > 0 { - bpi.Variables = make([]api.Variable, len(bp.Variables)) - } - for i := range bp.Variables { - v, err := s.EvalVariable(bp.Variables[i]) - if err != nil { - return err + if len(bp.Variables) > 0 { + bpi.Variables = make([]api.Variable, len(bp.Variables)) + } + for i := range bp.Variables { + v, err := s.EvalVariable(bp.Variables[i]) + if err != nil { + return err + } + bpi.Variables[i] = *api.ConvertVar(v) + } + vars, err := s.FunctionArguments() + if err == nil { + bpi.Arguments = convertVars(vars) } - bpi.Variables[i] = *api.ConvertVar(v) - } - args, err := s.FunctionArguments() - if err == nil { - bpi.Arguments = convertVars(args) } + return nil } diff --git a/service/rpc/client.go b/service/rpc/client.go index 32a35bcb908d8d35cb21bc05311021fc769f9cb0..96b759f0c2e79fbab5ca66f9955202b91b3ffad0 100644 --- a/service/rpc/client.go +++ b/service/rpc/client.go @@ -66,7 +66,21 @@ func (c *RPCClient) Continue() <-chan *api.DebuggerState { state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus) } ch <- state - if err != nil || state.Exited || state.Breakpoint == nil || !state.Breakpoint.Tracepoint { + if err != nil || state.Exited { + close(ch) + return + } + + isbreakpoint := false + istracepoint := true + for i := range state.Threads { + if state.Threads[i].Breakpoint != nil { + isbreakpoint = true + istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint + } + } + + if !isbreakpoint || !istracepoint { close(ch) return } diff --git a/service/test/integration_test.go b/service/test/integration_test.go index 18632ad00564322217a9eab775bf0291131a336b..25b3744a2ec8965ffbe0f791a3c2451e1eb56e93 100644 --- a/service/test/integration_test.go +++ b/service/test/integration_test.go @@ -96,7 +96,7 @@ func TestRestart_duringStop(t *testing.T) { t.Fatal(err) } state := <-c.Continue() - if state.Breakpoint == nil { + if state.CurrentThread.Breakpoint == nil { t.Fatal("did not hit breakpoint") } if err := c.Restart(); err != nil { @@ -433,12 +433,12 @@ func TestClientServer_traceContinue(t *testing.T) { count := 0 contChan := c.Continue() for state := range contChan { - if state.Breakpoint != nil { + if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil { count++ t.Logf("%v", state) - bpi := state.BreakpointInfo + bpi := state.CurrentThread.BreakpointInfo if bpi.Goroutine == nil { t.Fatalf("No goroutine information") @@ -494,8 +494,8 @@ func TestClientServer_traceContinue2(t *testing.T) { countSayhi := 0 contChan := c.Continue() for state := range contChan { - if state.Breakpoint != nil { - switch state.Breakpoint.ID { + if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil { + switch state.CurrentThread.Breakpoint.ID { case bp1.ID: countMain++ case bp2.ID: diff --git a/terminal/command.go b/terminal/command.go index fc627e7cec8f45247daef1daf0c4fee665857feb..65e149224092990c65f152d016abd21003491ae4 100644 --- a/terminal/command.go +++ b/terminal/command.go @@ -837,6 +837,15 @@ func printStack(stack []api.Stackframe, ind string) { } func printcontext(t *Term, state *api.DebuggerState) error { + for i := range state.Threads { + if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) { + continue + } + if state.Threads[i].Breakpoint != nil { + printcontextThread(t, state.Threads[i]) + } + } + if state.CurrentThread == nil { fmt.Println("No current thread available") return nil @@ -846,64 +855,70 @@ func printcontext(t *Term, state *api.DebuggerState) error { t.Println("=>", "no source available") return nil } - var fn *api.Function - if state.CurrentThread.Function != nil { - fn = state.CurrentThread.Function + + printcontextThread(t, state.CurrentThread) + + if state.CurrentThread.Breakpoint == nil || !state.CurrentThread.Breakpoint.Tracepoint { + return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) } + return nil +} - if state.Breakpoint != nil { - args := "" - if state.Breakpoint.Tracepoint { - var arg []string - for _, ar := range state.CurrentThread.Function.Args { - arg = append(arg, ar.SinglelineString()) - } - args = strings.Join(arg, ", ") - } - - if hitCount, ok := state.Breakpoint.HitCount[strconv.Itoa(state.SelectedGoroutine.ID)]; ok { - fmt.Printf("> %s(%s) %s:%d (hits goroutine(%d):%d total:%d)\n", - fn.Name, - args, - ShortenFilePath(state.CurrentThread.File), - state.CurrentThread.Line, - state.SelectedGoroutine.ID, - hitCount, - state.Breakpoint.TotalHitCount) - } else { - fmt.Printf("> %s(%s) %s:%d (hits total:%d)\n", - fn.Name, - args, - ShortenFilePath(state.CurrentThread.File), - state.CurrentThread.Line, - state.Breakpoint.TotalHitCount) +func printcontextThread(t *Term, th *api.Thread) { + fn := th.Function + + if th.Breakpoint == nil { + fmt.Printf("> %s() %s:%d\n", fn.Name, ShortenFilePath(th.File), th.Line) + return + } + + args := "" + if th.Breakpoint.Tracepoint && fn != nil { + var arg []string + for _, ar := range fn.Args { + arg = append(arg, ar.SinglelineString()) } + args = strings.Join(arg, ", ") + } + + if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok { + fmt.Printf("> %s(%s) %s:%d (hits goroutine(%d):%d total:%d)\n", + fn.Name, + args, + ShortenFilePath(th.File), + th.Line, + th.GoroutineID, + hitCount, + th.Breakpoint.TotalHitCount) } else { - fmt.Printf("> %s() %s:%d\n", fn.Name, ShortenFilePath(state.CurrentThread.File), state.CurrentThread.Line) + fmt.Printf("> %s(%s) %s:%d (hits total:%d)\n", + fn.Name, + args, + ShortenFilePath(th.File), + th.Line, + th.Breakpoint.TotalHitCount) } - if state.BreakpointInfo != nil { - bpi := state.BreakpointInfo + if th.BreakpointInfo != nil { + bpi := th.BreakpointInfo if bpi.Goroutine != nil { writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t") } - ss := make([]string, len(bpi.Variables)) - for i, v := range bpi.Variables { - ss[i] = fmt.Sprintf("%s: %v", v.Name, v.MultilineString("")) + if len(bpi.Variables) > 0 { + ss := make([]string, len(bpi.Variables)) + for i, v := range bpi.Variables { + ss[i] = fmt.Sprintf("%s: %s", v.Name, v.MultilineString("")) + } + fmt.Printf("\t%s\n", strings.Join(ss, ", ")) } - fmt.Printf("\t%s\n", strings.Join(ss, ", ")) if bpi.Stacktrace != nil { fmt.Printf("\tStack:\n") printStack(bpi.Stacktrace, "\t\t") } } - if state.Breakpoint != nil && state.Breakpoint.Tracepoint { - return nil - } - return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) } func printfile(t *Term, filename string, line int, showArrow bool) error {