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 (
|
2016-02-02 11:26:29 +00:00
|
|
|
"errors"
|
2015-08-10 13:55:57 +00:00
|
|
|
"fmt"
|
2017-02-08 00:23:47 +00:00
|
|
|
|
2017-02-08 16:00:44 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/frame"
|
2017-10-05 07:26:19 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/op"
|
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 {
|
2017-04-21 06:55:53 +00:00
|
|
|
Fn string
|
2015-08-20 14:28:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (nra NoReturnAddr) Error() string {
|
2017-04-21 06:55:53 +00:00
|
|
|
return fmt.Sprintf("could not find return address for %s", nra.Fn)
|
2015-08-20 14:28:11 +00:00
|
|
|
}
|
|
|
|
|
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
|
2017-10-05 07:26:19 +00:00
|
|
|
// Frame registers.
|
|
|
|
Regs op.DwarfRegisters
|
2017-05-16 18:23:33 +00:00
|
|
|
// High address of the stack.
|
|
|
|
StackHi uint64
|
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
|
2017-06-23 11:31:05 +00:00
|
|
|
// Err is set if an error occoured during stacktrace
|
|
|
|
Err error
|
2015-09-17 08:42:34 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 22:57:47 +00:00
|
|
|
// ThreadStacktrace 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.
|
2017-04-21 07:50:38 +00:00
|
|
|
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
2017-10-05 07:26:19 +00:00
|
|
|
regs, err := thread.Registers(true)
|
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
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs), 0, nil, -1)
|
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
|
|
|
|
}
|
2017-04-21 06:55:53 +00:00
|
|
|
if g.Thread != nil {
|
2017-10-05 07:26:19 +00:00
|
|
|
regs, err := g.Thread.Registers(true)
|
2017-02-15 13:41:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs), g.stackhi, stkbar, g.stkbarPos), nil
|
2016-03-18 08:51:48 +00:00
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, 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
|
|
|
// 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 {
|
2017-10-05 07:26:19 +00:00
|
|
|
pc uint64
|
|
|
|
top bool
|
|
|
|
atend bool
|
|
|
|
frame Stackframe
|
|
|
|
bi *BinaryInfo
|
|
|
|
mem MemoryReadWriter
|
|
|
|
err error
|
2015-10-16 06:42:02 +00:00
|
|
|
|
2017-05-16 18:23:33 +00:00
|
|
|
stackhi uint64
|
2017-02-08 13:14:57 +00:00
|
|
|
stackBarrierPC uint64
|
|
|
|
stkbar []savedLR
|
2017-10-05 07:26:19 +00:00
|
|
|
|
|
|
|
// regs is the register set for the next frame, callFrameRegs is the
|
|
|
|
// register set for the call frame of the next frame.
|
|
|
|
regs, callFrameRegs op.DwarfRegisters
|
2017-02-08 13:14:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type savedLR struct {
|
|
|
|
ptr uint64
|
|
|
|
val uint64
|
|
|
|
}
|
|
|
|
|
2017-10-05 07:26:19 +00:00
|
|
|
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
2017-09-01 13:30:45 +00:00
|
|
|
stackBarrierFunc := bi.LookupFunc[runtimeStackBarrier] // stack barriers were removed in Go 1.9
|
2017-03-13 17:53:16 +00:00
|
|
|
var stackBarrierPC uint64
|
|
|
|
if stackBarrierFunc != nil && stkbar != nil {
|
|
|
|
stackBarrierPC = stackBarrierFunc.Entry
|
2017-10-05 07:26:19 +00:00
|
|
|
fn := bi.PCToFunc(regs.PC())
|
2017-02-08 13:14:57 +00:00
|
|
|
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.
|
2017-10-05 07:26:19 +00:00
|
|
|
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
2017-02-08 13:14:57 +00:00
|
|
|
// runtime.stackBarrier has not incremented stkbarPos.
|
2017-10-05 07:26:19 +00:00
|
|
|
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() {
|
2017-02-08 13:14:57 +00:00
|
|
|
// runtime.stackBarrier has incremented stkbarPos.
|
|
|
|
stkbarPos--
|
|
|
|
} else {
|
2017-10-05 07:26:19 +00:00
|
|
|
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())}
|
2017-02-08 13:14:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
stkbar = stkbar[stkbarPos:]
|
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, 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
|
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
ret, retaddr := it.advanceRegs()
|
|
|
|
it.frame = it.newStackframe(ret, retaddr)
|
2015-10-16 06:42:02 +00:00
|
|
|
|
|
|
|
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.
|
2017-02-16 13:16:00 +00:00
|
|
|
if it.frame.Current.Fn != nil && (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
|
2017-10-05 07:26:19 +00:00
|
|
|
it.regs = it.callFrameRegs
|
2015-10-16 06:42:02 +00:00
|
|
|
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
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-10-05 07:26:19 +00:00
|
|
|
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
|
2015-08-28 20:06:29 +00:00
|
|
|
if retaddr == 0 {
|
2017-10-05 07:26:19 +00:00
|
|
|
it.err = NullAddrError{}
|
|
|
|
return Stackframe{}
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
f, l, fn := it.bi.PCToLine(it.pc)
|
|
|
|
if fn == nil {
|
|
|
|
f = "?"
|
|
|
|
l = -1
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, StackHi: it.stackhi}
|
|
|
|
if !it.top {
|
|
|
|
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(it.pc - 1)
|
|
|
|
if r.Call.Fn == nil {
|
|
|
|
r.Call.File = "?"
|
|
|
|
r.Call.Line = -1
|
|
|
|
}
|
2017-02-16 19:20:12 +00:00
|
|
|
r.Call.PC = r.Current.PC
|
2015-09-17 08:42:34 +00:00
|
|
|
} else {
|
|
|
|
r.Call = r.Current
|
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
return r
|
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 {
|
2017-06-23 11:31:05 +00:00
|
|
|
if len(frames) == 0 {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
frames = append(frames, Stackframe{Err: 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
|
|
|
}
|
2017-10-05 07:26:19 +00:00
|
|
|
|
|
|
|
// advanceRegs calculates it.callFrameRegs using it.regs and the frame
|
|
|
|
// descriptor entry for the current stack frame.
|
|
|
|
// it.regs.CallFrameCFA is updated.
|
|
|
|
func (it *stackIterator) advanceRegs() (ret uint64, retaddr uint64) {
|
|
|
|
fde, err := it.bi.frameEntries.FDEForPC(it.pc)
|
|
|
|
var framectx *frame.FrameContext
|
|
|
|
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
|
|
|
framectx = it.bi.Arch.FixFrameUnwindContext(nil)
|
|
|
|
} else {
|
|
|
|
framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc))
|
|
|
|
}
|
|
|
|
|
|
|
|
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
|
|
|
if cfareg == nil {
|
|
|
|
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
it.regs.CFA = int64(cfareg.Uint64Val)
|
|
|
|
|
|
|
|
it.callFrameRegs = op.DwarfRegisters{ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum}
|
|
|
|
|
|
|
|
// According to the standard the compiler should be responsible for emitting
|
|
|
|
// rules for the RSP register so that it can then be used to calculate CFA,
|
|
|
|
// however neither Go nor GCC do this.
|
|
|
|
// In the following line we copy GDB's behaviour by assuming this is
|
|
|
|
// implicit.
|
|
|
|
// See also the comment in dwarf2_frame_default_init in
|
|
|
|
// $GDB_SOURCE/dwarf2-frame.c
|
|
|
|
it.callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
|
|
|
|
|
|
|
for i, regRule := range framectx.Regs {
|
|
|
|
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
|
|
|
it.callFrameRegs.AddReg(i, reg)
|
|
|
|
if i == framectx.RetAddrReg {
|
|
|
|
if reg == nil {
|
|
|
|
if err == nil {
|
|
|
|
err = fmt.Errorf("Undefined return address at %#x", it.pc)
|
|
|
|
}
|
|
|
|
it.err = err
|
|
|
|
} else {
|
|
|
|
ret = reg.Uint64Val
|
|
|
|
}
|
|
|
|
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, retaddr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, cfa int64) (*op.DwarfRegister, error) {
|
|
|
|
switch rule.Rule {
|
|
|
|
default:
|
|
|
|
fallthrough
|
|
|
|
case frame.RuleUndefined:
|
|
|
|
return nil, nil
|
|
|
|
case frame.RuleSameVal:
|
|
|
|
return it.regs.Reg(regnum), nil
|
|
|
|
case frame.RuleOffset:
|
|
|
|
return it.readRegisterAt(regnum, uint64(cfa+rule.Offset))
|
|
|
|
case frame.RuleValOffset:
|
|
|
|
return op.DwarfRegisterFromUint64(uint64(cfa + rule.Offset)), nil
|
|
|
|
case frame.RuleRegister:
|
|
|
|
return it.regs.Reg(rule.Reg), nil
|
|
|
|
case frame.RuleExpression:
|
|
|
|
v, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return it.readRegisterAt(regnum, uint64(v))
|
|
|
|
case frame.RuleValExpression:
|
|
|
|
v, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return op.DwarfRegisterFromUint64(uint64(v)), nil
|
|
|
|
case frame.RuleArchitectural:
|
|
|
|
return nil, errors.New("architectural frame rules are unsupported")
|
|
|
|
case frame.RuleCFA:
|
|
|
|
if it.regs.Reg(rule.Reg) == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return op.DwarfRegisterFromUint64(uint64(int64(it.regs.Uint64Val(rule.Reg)) + rule.Offset)), nil
|
|
|
|
case frame.RuleRegOffset:
|
|
|
|
if it.regs.Reg(rule.Reg) == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return it.readRegisterAt(regnum, uint64(int64(it.regs.Uint64Val(rule.Reg))+rule.Offset))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) {
|
|
|
|
buf := make([]byte, it.bi.Arch.RegSize(regnum))
|
|
|
|
_, err := it.mem.ReadMemory(buf, uintptr(addr))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return op.DwarfRegisterFromBytes(buf), nil
|
|
|
|
}
|