2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2015-04-19 22:11:33 +00:00
|
|
|
|
2015-08-10 13:55:57 +00:00
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2016-02-02 11:26:29 +00:00
|
|
|
"errors"
|
2015-08-10 13:55:57 +00:00
|
|
|
"fmt"
|
2016-03-18 08:32:17 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
2015-08-10 13:55:57 +00:00
|
|
|
)
|
2015-04-19 22:11:33 +00:00
|
|
|
|
2017-02-08 13:14:57 +00:00
|
|
|
// This code is partly adaped from runtime.gentraceback in
|
|
|
|
// $GOROOT/src/runtime/traceback.go
|
|
|
|
|
|
|
|
const runtimeStackBarrier = "runtime.stackBarrier"
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// NoReturnAddr is returned when return address
|
2016-05-13 18:28:56 +00:00
|
|
|
// could not be found during stack trace.
|
2015-08-20 14:28:11 +00:00
|
|
|
type NoReturnAddr struct {
|
|
|
|
fn string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nra NoReturnAddr) Error() string {
|
|
|
|
return fmt.Sprintf("could not find return address for %s", nra.fn)
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Stackframe represents a frame in a system stack.
|
2015-08-28 20:06:29 +00:00
|
|
|
type Stackframe struct {
|
2015-09-20 04:19:03 +00:00
|
|
|
// Address the function above this one on the call stack will return to.
|
2015-09-17 08:42:34 +00:00
|
|
|
Current Location
|
|
|
|
// Address of the call instruction for the function above on the call stack.
|
|
|
|
Call Location
|
2016-06-21 20:05:28 +00:00
|
|
|
// Start address of the stack frame.
|
2016-07-03 07:02:21 +00:00
|
|
|
CFA int64
|
2016-06-21 20:05:28 +00:00
|
|
|
// Description of the stack frame.
|
2016-07-03 07:02:21 +00:00
|
|
|
FDE *frame.FrameDescriptionEntry
|
2016-06-21 20:05:28 +00:00
|
|
|
// Return address for this stack frame (as read from the stack frame itself).
|
2016-07-03 07:02:21 +00:00
|
|
|
Ret uint64
|
2017-02-08 13:14:57 +00:00
|
|
|
// Address to the memory location containing the return address
|
|
|
|
addrret uint64
|
2015-09-17 08:42:34 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Scope returns a new EvalScope using this frame.
|
2015-09-17 08:42:34 +00:00
|
|
|
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
|
|
|
|
return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// ReturnAddress returns the return address of the function
|
|
|
|
// this thread is executing.
|
|
|
|
func (t *Thread) ReturnAddress() (uint64, error) {
|
|
|
|
locations, err := t.Stacktrace(2)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
if len(locations) < 2 {
|
2015-09-17 08:42:34 +00:00
|
|
|
return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
|
2015-08-10 13:55:57 +00:00
|
|
|
}
|
2015-09-17 08:42:34 +00:00
|
|
|
return locations[1].Current.PC, nil
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 13:14:57 +00:00
|
|
|
func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, error) {
|
2016-11-15 16:16:33 +00:00
|
|
|
regs, err := t.Registers(false)
|
2016-03-18 08:51:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-08 13:14:57 +00:00
|
|
|
return newStackIterator(t.dbp, regs.PC(), regs.SP(), stkbar, stkbarPos), nil
|
2016-03-18 08:51:48 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Stacktrace returns the stack trace for thread.
|
2015-07-01 03:16:52 +00:00
|
|
|
// Note the locations in the array are return addresses not call addresses.
|
2016-01-10 08:57:52 +00:00
|
|
|
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
2017-02-08 13:14:57 +00:00
|
|
|
it, err := t.stackIterator(nil, -1)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
2015-07-01 03:16:52 +00:00
|
|
|
return nil, err
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2016-03-18 08:51:48 +00:00
|
|
|
return it.stacktrace(depth)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
2016-03-18 08:51:48 +00:00
|
|
|
func (g *G) stackIterator() (*stackIterator, error) {
|
2017-02-08 13:14:57 +00:00
|
|
|
stkbar, err := g.stkbar()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-06-17 17:11:57 +00:00
|
|
|
if g.thread != nil {
|
2017-02-08 13:14:57 +00:00
|
|
|
return g.thread.stackIterator(stkbar, g.stkbarPos)
|
2016-03-18 08:51:48 +00:00
|
|
|
}
|
2017-02-08 13:14:57 +00:00
|
|
|
return newStackIterator(g.dbp, g.PC, g.SP, stkbar, g.stkbarPos), nil
|
2016-03-18 08:51:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
2016-03-18 08:51:48 +00:00
|
|
|
return it.stacktrace(depth)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// GoroutineLocation returns the location of the given
|
|
|
|
// goroutine.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) GoroutineLocation(g *G) *Location {
|
2015-06-17 17:11:57 +00:00
|
|
|
f, l, fn := dbp.PCToLine(g.PC)
|
|
|
|
return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// NullAddrError is an error for a null address.
|
2015-05-07 21:55:06 +00:00
|
|
|
type NullAddrError struct{}
|
|
|
|
|
|
|
|
func (n NullAddrError) Error() string {
|
|
|
|
return "NULL address"
|
|
|
|
}
|
|
|
|
|
2016-03-18 08:51:48 +00:00
|
|
|
// stackIterator holds information
|
2016-01-10 08:57:52 +00:00
|
|
|
// required to iterate and walk the program
|
|
|
|
// stack.
|
2016-03-18 08:51:48 +00:00
|
|
|
type stackIterator struct {
|
2015-10-16 06:42:02 +00:00
|
|
|
pc, sp uint64
|
|
|
|
top bool
|
2016-03-09 15:11:58 +00:00
|
|
|
atend bool
|
2015-10-16 06:42:02 +00:00
|
|
|
frame Stackframe
|
|
|
|
dbp *Process
|
|
|
|
err error
|
|
|
|
|
2017-02-08 13:14:57 +00:00
|
|
|
stackBarrierPC uint64
|
|
|
|
stkbar []savedLR
|
|
|
|
}
|
|
|
|
|
|
|
|
type savedLR struct {
|
|
|
|
ptr uint64
|
|
|
|
val uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStackIterator(dbp *Process, pc, sp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
|
|
|
stackBarrierPC := dbp.goSymTable.LookupFunc(runtimeStackBarrier).Entry
|
|
|
|
if stkbar != nil {
|
|
|
|
fn := dbp.goSymTable.PCToFunc(pc)
|
|
|
|
if fn != nil && fn.Name == runtimeStackBarrier {
|
|
|
|
// We caught the goroutine as it's executing the stack barrier, we must
|
|
|
|
// determine whether or not g.stackPos has already been incremented or not.
|
|
|
|
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < sp {
|
|
|
|
// runtime.stackBarrier has not incremented stkbarPos.
|
|
|
|
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < sp {
|
|
|
|
// runtime.stackBarrier has incremented stkbarPos.
|
|
|
|
stkbarPos--
|
|
|
|
} else {
|
|
|
|
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", sp)}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stkbar = stkbar[stkbarPos:]
|
|
|
|
}
|
|
|
|
return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
|
2015-10-16 06:42:02 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Next points the iterator to the next stack frame.
|
2016-03-18 08:51:48 +00:00
|
|
|
func (it *stackIterator) Next() bool {
|
2015-10-16 06:42:02 +00:00
|
|
|
if it.err != nil || it.atend {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
|
|
|
|
if it.err != nil {
|
2016-03-18 08:32:17 +00:00
|
|
|
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
2016-05-29 19:20:09 +00:00
|
|
|
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
2016-03-18 08:32:17 +00:00
|
|
|
it.atend = true
|
|
|
|
it.err = nil
|
|
|
|
return true
|
|
|
|
}
|
2015-10-16 06:42:02 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if it.frame.Current.Fn == nil {
|
2016-03-23 12:36:51 +00:00
|
|
|
if it.top {
|
|
|
|
it.err = fmt.Errorf("PC not associated to any function")
|
|
|
|
}
|
2015-10-16 06:42:02 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if it.frame.Ret <= 0 {
|
|
|
|
it.atend = true
|
|
|
|
return true
|
|
|
|
}
|
2017-02-08 13:14:57 +00:00
|
|
|
|
|
|
|
if it.stkbar != nil && it.frame.Ret == it.stackBarrierPC && it.frame.addrret == it.stkbar[0].ptr {
|
|
|
|
// Skip stack barrier frames
|
|
|
|
it.frame.Ret = it.stkbar[0].val
|
|
|
|
it.stkbar = it.stkbar[1:]
|
|
|
|
}
|
|
|
|
|
2015-10-16 06:42:02 +00:00
|
|
|
// Look for "top of stack" functions.
|
2016-03-23 08:16:48 +00:00
|
|
|
if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall" {
|
2015-10-16 06:42:02 +00:00
|
|
|
it.atend = true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
it.top = false
|
|
|
|
it.pc = it.frame.Ret
|
|
|
|
it.sp = uint64(it.frame.CFA)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Frame returns the frame the iterator is pointing at.
|
2016-03-18 08:51:48 +00:00
|
|
|
func (it *stackIterator) Frame() Stackframe {
|
2015-10-16 06:42:02 +00:00
|
|
|
if it.err != nil {
|
|
|
|
panic(it.err)
|
|
|
|
}
|
|
|
|
return it.frame
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Err returns the error encountered during stack iteration.
|
2016-03-18 08:51:48 +00:00
|
|
|
func (it *stackIterator) Err() error {
|
2015-10-16 06:42:02 +00:00
|
|
|
return it.err
|
|
|
|
}
|
|
|
|
|
2015-09-17 08:42:34 +00:00
|
|
|
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
|
2015-07-01 03:16:52 +00:00
|
|
|
f, l, fn := dbp.PCToLine(pc)
|
2015-08-28 20:06:29 +00:00
|
|
|
fde, err := dbp.frameEntries.FDEForPC(pc)
|
|
|
|
if err != nil {
|
|
|
|
return Stackframe{}, err
|
|
|
|
}
|
|
|
|
spoffset, retoffset := fde.ReturnAddressOffset(pc)
|
|
|
|
cfa := int64(sp) + spoffset
|
|
|
|
|
|
|
|
retaddr := uintptr(cfa + retoffset)
|
|
|
|
if retaddr == 0 {
|
|
|
|
return Stackframe{}, NullAddrError{}
|
|
|
|
}
|
|
|
|
data, err := dbp.CurrentThread.readMemory(retaddr, dbp.arch.PtrSize())
|
|
|
|
if err != nil {
|
|
|
|
return Stackframe{}, err
|
|
|
|
}
|
2017-02-08 13:14:57 +00:00
|
|
|
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: binary.LittleEndian.Uint64(data), addrret: uint64(retaddr)}
|
2015-09-17 08:42:34 +00:00
|
|
|
if !top {
|
|
|
|
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
|
|
|
r.Call.PC, _, _ = dbp.goSymTable.LineToPC(r.Call.File, r.Call.Line)
|
|
|
|
} else {
|
|
|
|
r.Call = r.Current
|
|
|
|
}
|
|
|
|
return r, nil
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-18 08:51:48 +00:00
|
|
|
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
2016-02-02 11:26:29 +00:00
|
|
|
if depth < 0 {
|
|
|
|
return nil, errors.New("negative maximum stack depth")
|
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
frames := make([]Stackframe, 0, depth+1)
|
2015-10-16 06:42:02 +00:00
|
|
|
for it.Next() {
|
|
|
|
frames = append(frames, it.Frame())
|
|
|
|
if len(frames) >= depth+1 {
|
2015-06-17 17:11:57 +00:00
|
|
|
break
|
|
|
|
}
|
2015-10-16 06:42:02 +00:00
|
|
|
}
|
|
|
|
if err := it.Err(); err != nil {
|
|
|
|
return nil, err
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
return frames, nil
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|