diff --git a/proc/proc.go b/proc/proc.go index d329b978..79e77a3a 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -876,7 +876,7 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) { out.Thread = g.thread } - locs, err := dbp.GoroutineStacktrace(g, frame) + locs, err := g.Stacktrace(frame) if err != nil { return nil, err } diff --git a/proc/proc_test.go b/proc/proc_test.go index f7e02b6e..aa669f1d 100644 --- a/proc/proc_test.go +++ b/proc/proc_test.go @@ -758,7 +758,7 @@ func TestStacktraceGoroutine(t *testing.T) { mainCount := 0 for i, g := range gs { - locations, err := p.GoroutineStacktrace(g, 40) + locations, err := g.Stacktrace(40) assertNoError(err, t, "GoroutineStacktrace()") if stackMatch(mainStack, locations, false) { @@ -1053,7 +1053,7 @@ func TestFrameEvaluation(t *testing.T) { found := make([]bool, 10) for _, g := range gs { frame := -1 - frames, err := p.GoroutineStacktrace(g, 10) + frames, err := g.Stacktrace(10) assertNoError(err, t, "GoroutineStacktrace()") for i := range frames { if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" { diff --git a/proc/stack.go b/proc/stack.go index 6e3411f6..90044823 100644 --- a/proc/stack.go +++ b/proc/stack.go @@ -45,24 +45,39 @@ func (t *Thread) ReturnAddress() (uint64, error) { return locations[1].Current.PC, nil } -// Stacktrace returns the stack trace for thread. -// Note the locations in the array are return addresses not call addresses. -func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) { +func (t *Thread) stackIterator() (*stackIterator, error) { regs, err := t.Registers() if err != nil { return nil, err } - return t.dbp.stacktrace(regs.PC(), regs.SP(), depth) + return newStackIterator(t.dbp, regs.PC(), regs.SP()), nil } -// GoroutineStacktrace returns the stack trace for a goroutine. +// Stacktrace returns the stack trace for thread. // Note the locations in the array are return addresses not call addresses. -func (dbp *Process) GoroutineStacktrace(g *G, depth int) ([]Stackframe, error) { - if g.thread != nil { - return g.thread.Stacktrace(depth) +func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) { + it, err := t.stackIterator() + if err != nil { + return nil, err } - locs, err := dbp.stacktrace(g.PC, g.SP, depth) - return locs, err + return it.stacktrace(depth) +} + +func (g *G) stackIterator() (*stackIterator, error) { + if g.thread != nil { + return g.thread.stackIterator() + } + return newStackIterator(g.dbp, g.PC, g.SP), nil +} + +// Stacktrace returns the stack trace for a goroutine. +// Note the locations in the array are return addresses not call addresses. +func (g *G) Stacktrace(depth int) ([]Stackframe, error) { + it, err := g.stackIterator() + if err != nil { + return nil, err + } + return it.stacktrace(depth) } // GoroutineLocation returns the location of the given @@ -79,10 +94,10 @@ func (n NullAddrError) Error() string { return "NULL address" } -// StackIterator holds information +// stackIterator holds information // required to iterate and walk the program // stack. -type StackIterator struct { +type stackIterator struct { pc, sp uint64 top bool atend bool @@ -91,12 +106,12 @@ type StackIterator struct { err error } -func newStackIterator(dbp *Process, pc, sp uint64) *StackIterator { - return &StackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false} +func newStackIterator(dbp *Process, pc, sp uint64) *stackIterator { + return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false} } // Next points the iterator to the next stack frame. -func (it *StackIterator) Next() bool { +func (it *stackIterator) Next() bool { if it.err != nil || it.atend { return false } @@ -132,7 +147,7 @@ func (it *StackIterator) Next() bool { } // Frame returns the frame the iterator is pointing at. -func (it *StackIterator) Frame() Stackframe { +func (it *stackIterator) Frame() Stackframe { if it.err != nil { panic(it.err) } @@ -140,7 +155,7 @@ func (it *StackIterator) Frame() Stackframe { } // Err returns the error encountered during stack iteration. -func (it *StackIterator) Err() error { +func (it *stackIterator) Err() error { return it.err } @@ -171,12 +186,11 @@ func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) { return r, nil } -func (dbp *Process) stacktrace(pc, sp uint64, depth int) ([]Stackframe, error) { +func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) { if depth < 0 { return nil, errors.New("negative maximum stack depth") } frames := make([]Stackframe, 0, depth+1) - it := newStackIterator(dbp, pc, sp) for it.Next() { frames = append(frames, it.Frame()) if len(frames) >= depth+1 { diff --git a/proc/variables.go b/proc/variables.go index 3c36faa4..73ac2898 100644 --- a/proc/variables.go +++ b/proc/variables.go @@ -317,7 +317,7 @@ func (g *G) ChanRecvBlocked() bool { // chanRecvReturnAddr returns the address of the return from a channel read. func (g *G) chanRecvReturnAddr(dbp *Process) (uint64, error) { - locs, err := dbp.GoroutineStacktrace(g, 4) + locs, err := g.Stacktrace(4) if err != nil { return 0, err } @@ -411,15 +411,10 @@ func isExportedRuntime(name string) bool { // UserCurrent returns the location the users code is at, // or was at before entering a runtime function. func (g *G) UserCurrent() Location { - pc, sp := g.PC, g.SP - if g.thread != nil { - regs, err := g.thread.Registers() - if err != nil { - return g.CurrentLoc - } - pc, sp = regs.PC(), regs.SP() + it, err := g.stackIterator() + if err != nil { + return g.CurrentLoc } - it := newStackIterator(g.dbp, pc, sp) for it.Next() { frame := it.Frame() if frame.Call.Fn != nil { diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 14f8de0e..0cdb5569 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -691,7 +691,7 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, full bool) ([]api.Stackfra if g == nil { rawlocs, err = d.process.CurrentThread.Stacktrace(depth) } else { - rawlocs, err = d.process.GoroutineStacktrace(g, depth) + rawlocs, err = g.Stacktrace(depth) } if err != nil { return nil, err