service,terminal: propagating simultaneous breakpoints
This commit is contained in:
parent
a9e2696f46
commit
708cf2f290
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
if bp.Goroutine {
|
||||
g, err := d.process.CurrentThread.GetG()
|
||||
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
|
||||
}
|
||||
bpi.Goroutine = api.ConvertGoroutine(g)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
s, err := d.process.CurrentThread.Scope()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Goroutine = api.ConvertGoroutine(g)
|
||||
}
|
||||
|
||||
if bp.Stacktrace > 0 {
|
||||
rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(bp.Variables) > 0 {
|
||||
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||
}
|
||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, false)
|
||||
if err != nil {
|
||||
return err
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
bpi.Variables[i] = *api.ConvertVar(v)
|
||||
}
|
||||
args, err := s.FunctionArguments()
|
||||
if err == nil {
|
||||
bpi.Arguments = convertVars(args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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, ", ")
|
||||
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 := 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)
|
||||
}
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user