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)
|
fmt.Fprintln(os.Stderr, state.Err)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var args []string
|
for i := range state.Threads {
|
||||||
var fname string
|
th := state.Threads[i]
|
||||||
if state.CurrentThread != nil && state.CurrentThread.Function != nil {
|
if th.Breakpoint == nil {
|
||||||
fname = state.CurrentThread.Function.Name
|
continue
|
||||||
}
|
|
||||||
if state.BreakpointInfo != nil {
|
|
||||||
for _, arg := range state.BreakpointInfo.Arguments {
|
|
||||||
args = append(args, arg.SinglelineString())
|
|
||||||
}
|
}
|
||||||
|
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:
|
case <-sigChan:
|
||||||
server.Stop(traceAttachPid == 0)
|
server.Stop(traceAttachPid == 0)
|
||||||
return 1
|
return 1
|
||||||
|
@ -37,6 +37,7 @@ func ConvertThread(th *proc.Thread) *Thread {
|
|||||||
file string
|
file string
|
||||||
line int
|
line int
|
||||||
pc uint64
|
pc uint64
|
||||||
|
gid int
|
||||||
)
|
)
|
||||||
|
|
||||||
loc, err := th.Location()
|
loc, err := th.Location()
|
||||||
@ -47,12 +48,24 @@ func ConvertThread(th *proc.Thread) *Thread {
|
|||||||
function = ConvertFunction(loc.Fn)
|
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{
|
return &Thread{
|
||||||
ID: th.Id,
|
ID: th.Id,
|
||||||
PC: pc,
|
PC: pc,
|
||||||
File: file,
|
File: file,
|
||||||
Line: line,
|
Line: line,
|
||||||
Function: function,
|
Function: function,
|
||||||
|
GoroutineID: gid,
|
||||||
|
Breakpoint: bp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,19 +4,15 @@ import "reflect"
|
|||||||
|
|
||||||
// DebuggerState represents the current context of the debugger.
|
// DebuggerState represents the current context of the debugger.
|
||||||
type DebuggerState struct {
|
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 is the currently selected debugger thread.
|
||||||
CurrentThread *Thread `json:"currentThread,omitempty"`
|
CurrentThread *Thread `json:"currentThread,omitempty"`
|
||||||
// SelectedGoroutine is the currently selected goroutine
|
// SelectedGoroutine is the currently selected goroutine
|
||||||
SelectedGoroutine *Goroutine `json:"currentGoroutine,omitempty"`
|
SelectedGoroutine *Goroutine `json:"currentGoroutine,omitempty"`
|
||||||
// Information requested by the current breakpoint
|
// List of all the process threads
|
||||||
BreakpointInfo *BreakpointInfo `json:"breakPointInfo,omitrempty"`
|
Threads []*Thread
|
||||||
// Exited indicates whether the debugged process has exited.
|
// Exited indicates whether the debugged process has exited.
|
||||||
Exited bool `json:"exited"`
|
Exited bool `json:"exited"`
|
||||||
ExitStatus int `json:"exitStatus"`
|
ExitStatus int `json:"exitStatus"`
|
||||||
|
|
||||||
// Filled by RPCClient.Continue, indicates an error
|
// Filled by RPCClient.Continue, indicates an error
|
||||||
Err error `json:"-"`
|
Err error `json:"-"`
|
||||||
}
|
}
|
||||||
@ -62,6 +58,14 @@ type Thread struct {
|
|||||||
Line int `json:"line"`
|
Line int `json:"line"`
|
||||||
// Function is function information at the program counter. May be nil.
|
// Function is function information at the program counter. May be nil.
|
||||||
Function *Function `json:"function,omitempty"`
|
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 {
|
type Location struct {
|
||||||
|
@ -111,31 +111,26 @@ func (d *Debugger) State() (*api.DebuggerState, error) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
state *api.DebuggerState
|
state *api.DebuggerState
|
||||||
thread *api.Thread
|
|
||||||
goroutine *api.Goroutine
|
goroutine *api.Goroutine
|
||||||
)
|
)
|
||||||
|
|
||||||
if d.process.CurrentThread != nil {
|
|
||||||
thread = api.ConvertThread(d.process.CurrentThread)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.process.SelectedGoroutine != nil {
|
if d.process.SelectedGoroutine != nil {
|
||||||
goroutine = api.ConvertGoroutine(d.process.SelectedGoroutine)
|
goroutine = api.ConvertGoroutine(d.process.SelectedGoroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
var breakpoint *api.Breakpoint
|
|
||||||
bp := d.process.CurrentBreakpoint()
|
|
||||||
if bp != nil {
|
|
||||||
breakpoint = api.ConvertBreakpoint(bp)
|
|
||||||
}
|
|
||||||
|
|
||||||
state = &api.DebuggerState{
|
state = &api.DebuggerState{
|
||||||
Breakpoint: breakpoint,
|
|
||||||
CurrentThread: thread,
|
|
||||||
SelectedGoroutine: goroutine,
|
SelectedGoroutine: goroutine,
|
||||||
Exited: d.process.Exited(),
|
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
|
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 {
|
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
|
||||||
if state == nil || state.Breakpoint == nil {
|
if state == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
bp := state.Breakpoint
|
for i := range state.Threads {
|
||||||
bpi := &api.BreakpointInfo{}
|
if state.Threads[i].Breakpoint == nil {
|
||||||
state.BreakpointInfo = bpi
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if bp.Goroutine {
|
bp := state.Threads[i].Breakpoint
|
||||||
g, err := d.process.CurrentThread.GetG()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bpi.Goroutine = api.ConvertGoroutine(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bp.Stacktrace > 0 {
|
if len(bp.Variables) > 0 {
|
||||||
rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace)
|
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, false)
|
for i := range bp.Variables {
|
||||||
if err != nil {
|
v, err := s.EvalVariable(bp.Variables[i])
|
||||||
return err
|
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
|
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)
|
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
|
||||||
}
|
}
|
||||||
ch <- state
|
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)
|
close(ch)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ func TestRestart_duringStop(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
state := <-c.Continue()
|
state := <-c.Continue()
|
||||||
if state.Breakpoint == nil {
|
if state.CurrentThread.Breakpoint == nil {
|
||||||
t.Fatal("did not hit breakpoint")
|
t.Fatal("did not hit breakpoint")
|
||||||
}
|
}
|
||||||
if err := c.Restart(); err != nil {
|
if err := c.Restart(); err != nil {
|
||||||
@ -433,12 +433,12 @@ func TestClientServer_traceContinue(t *testing.T) {
|
|||||||
count := 0
|
count := 0
|
||||||
contChan := c.Continue()
|
contChan := c.Continue()
|
||||||
for state := range contChan {
|
for state := range contChan {
|
||||||
if state.Breakpoint != nil {
|
if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
|
||||||
count++
|
count++
|
||||||
|
|
||||||
t.Logf("%v", state)
|
t.Logf("%v", state)
|
||||||
|
|
||||||
bpi := state.BreakpointInfo
|
bpi := state.CurrentThread.BreakpointInfo
|
||||||
|
|
||||||
if bpi.Goroutine == nil {
|
if bpi.Goroutine == nil {
|
||||||
t.Fatalf("No goroutine information")
|
t.Fatalf("No goroutine information")
|
||||||
@ -494,8 +494,8 @@ func TestClientServer_traceContinue2(t *testing.T) {
|
|||||||
countSayhi := 0
|
countSayhi := 0
|
||||||
contChan := c.Continue()
|
contChan := c.Continue()
|
||||||
for state := range contChan {
|
for state := range contChan {
|
||||||
if state.Breakpoint != nil {
|
if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
|
||||||
switch state.Breakpoint.ID {
|
switch state.CurrentThread.Breakpoint.ID {
|
||||||
case bp1.ID:
|
case bp1.ID:
|
||||||
countMain++
|
countMain++
|
||||||
case bp2.ID:
|
case bp2.ID:
|
||||||
|
@ -837,6 +837,15 @@ func printStack(stack []api.Stackframe, ind string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printcontext(t *Term, state *api.DebuggerState) error {
|
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 {
|
if state.CurrentThread == nil {
|
||||||
fmt.Println("No current thread available")
|
fmt.Println("No current thread available")
|
||||||
return nil
|
return nil
|
||||||
@ -846,64 +855,70 @@ func printcontext(t *Term, state *api.DebuggerState) error {
|
|||||||
t.Println("=>", "no source available")
|
t.Println("=>", "no source available")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var fn *api.Function
|
|
||||||
if state.CurrentThread.Function != nil {
|
printcontextThread(t, state.CurrentThread)
|
||||||
fn = state.CurrentThread.Function
|
|
||||||
|
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 := ""
|
||||||
args := ""
|
if th.Breakpoint.Tracepoint && fn != nil {
|
||||||
if state.Breakpoint.Tracepoint {
|
var arg []string
|
||||||
var arg []string
|
for _, ar := range fn.Args {
|
||||||
for _, ar := range state.CurrentThread.Function.Args {
|
arg = append(arg, ar.SinglelineString())
|
||||||
arg = append(arg, ar.SinglelineString())
|
|
||||||
}
|
|
||||||
args = strings.Join(arg, ", ")
|
|
||||||
}
|
}
|
||||||
|
args = strings.Join(arg, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
if hitCount, ok := state.Breakpoint.HitCount[strconv.Itoa(state.SelectedGoroutine.ID)]; ok {
|
if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok {
|
||||||
fmt.Printf("> %s(%s) %s:%d (hits goroutine(%d):%d total:%d)\n",
|
fmt.Printf("> %s(%s) %s:%d (hits goroutine(%d):%d total:%d)\n",
|
||||||
fn.Name,
|
fn.Name,
|
||||||
args,
|
args,
|
||||||
ShortenFilePath(state.CurrentThread.File),
|
ShortenFilePath(th.File),
|
||||||
state.CurrentThread.Line,
|
th.Line,
|
||||||
state.SelectedGoroutine.ID,
|
th.GoroutineID,
|
||||||
hitCount,
|
hitCount,
|
||||||
state.Breakpoint.TotalHitCount)
|
th.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)
|
|
||||||
}
|
|
||||||
} else {
|
} 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 {
|
if th.BreakpointInfo != nil {
|
||||||
bpi := state.BreakpointInfo
|
bpi := th.BreakpointInfo
|
||||||
|
|
||||||
if bpi.Goroutine != nil {
|
if bpi.Goroutine != nil {
|
||||||
writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t")
|
writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t")
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := make([]string, len(bpi.Variables))
|
if len(bpi.Variables) > 0 {
|
||||||
for i, v := range bpi.Variables {
|
ss := make([]string, len(bpi.Variables))
|
||||||
ss[i] = fmt.Sprintf("%s: %v", v.Name, v.MultilineString(""))
|
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 {
|
if bpi.Stacktrace != nil {
|
||||||
fmt.Printf("\tStack:\n")
|
fmt.Printf("\tStack:\n")
|
||||||
printStack(bpi.Stacktrace, "\t\t")
|
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 {
|
func printfile(t *Term, filename string, line int, showArrow bool) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user