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 (
2017-08-24 07:46:47 +00:00
"debug/dwarf"
2016-02-02 11:26:29 +00:00
"errors"
2015-08-10 13:55:57 +00:00
"fmt"
2018-01-04 14:46:51 +00:00
"strings"
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
2018-03-20 10:05:35 +00:00
// This code is partly adapted from runtime.gentraceback in
2017-02-08 13:14:57 +00:00
// $GOROOT/src/runtime/traceback.go
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.
2017-09-01 13:34:13 +00:00
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
2018-03-20 10:05:35 +00:00
// Err is set if an error occurred during stacktrace
2017-06-23 11:31:05 +00:00
Err error
2017-09-01 13:34:13 +00:00
// SystemStack is true if this frame belongs to a system stack.
SystemStack bool
}
// FrameOffset returns the address of the stack frame, absolute for system
// stack frames or as an offset from stackhi for goroutine stacks (a
// negative value).
func ( frame * Stackframe ) FrameOffset ( ) int64 {
if frame . SystemStack {
return frame . Regs . CFA
}
return frame . Regs . CFA - int64 ( frame . stackHi )
}
// FramePointerOffset returns the value of the frame pointer, absolute for
// system stack frames or as an offset from stackhi for goroutine stacks (a
// negative value).
func ( frame * Stackframe ) FramePointerOffset ( ) int64 {
if frame . SystemStack {
return int64 ( frame . Regs . BP ( ) )
}
return int64 ( frame . Regs . BP ( ) ) - int64 ( frame . stackHi )
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-09-01 13:34:13 +00:00
g , _ := GetG ( thread )
if g == nil {
regs , err := thread . Registers ( true )
if err != nil {
return nil , err
}
it := newStackIterator ( thread . BinInfo ( ) , thread , thread . BinInfo ( ) . Arch . RegistersToDwarfRegisters ( regs ) , 0 , nil , - 1 , nil )
return it . stacktrace ( depth )
2015-04-19 22:11:33 +00:00
}
2017-09-01 13:34:13 +00:00
return g . 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-09-01 13:34:13 +00:00
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-09-01 13:34:13 +00:00
return newStackIterator ( g . variable . bi , g . Thread , g . variable . bi . Arch . RegistersToDwarfRegisters ( regs ) , g . stackhi , stkbar , g . stkbarPos , g ) , nil
2016-03-18 08:51:48 +00:00
}
2017-09-01 13:34:13 +00:00
return newStackIterator ( g . variable . bi , g . variable . mem , g . variable . bi . Arch . GoroutineToDwarfRegisters ( g ) , g . stackhi , stkbar , g . stkbarPos , g ) , 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-09-01 13:34:13 +00:00
systemstack bool
2017-02-08 13:14:57 +00:00
stackBarrierPC uint64
stkbar [ ] savedLR
2017-10-05 07:26:19 +00:00
2017-11-17 18:29:24 +00:00
// regs is the register set for the current frame
regs op . DwarfRegisters
2017-09-01 13:34:13 +00:00
g * G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
2017-02-08 13:14:57 +00:00
}
type savedLR struct {
ptr uint64
val uint64
}
2017-09-01 13:34:13 +00:00
func newStackIterator ( bi * BinaryInfo , mem MemoryReadWriter , regs op . DwarfRegisters , stackhi uint64 , stkbar [ ] savedLR , stkbarPos int , g * G ) * stackIterator {
stackBarrierFunc := bi . LookupFunc [ "runtime.stackBarrier" ] // 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-09-01 13:34:13 +00:00
if fn != nil && fn . Name == "runtime.stackBarrier" {
2017-02-08 13:14:57 +00:00
// 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-09-01 13:34:13 +00:00
var g0_sched_sp uint64
systemstack := true
if g != nil {
systemstack = g . SystemStack
g0var , _ := g . variable . fieldVariable ( "m" ) . structMember ( "g0" )
if g0var != nil {
g0 , _ := g0var . parseG ( )
if g0 != nil {
g0_sched_sp = g0 . SP
}
}
}
return & stackIterator { pc : regs . PC ( ) , regs : regs , top : true , bi : bi , mem : mem , err : nil , atend : false , stackhi : stackhi , stackBarrierPC : stackBarrierPC , stkbar : stkbar , systemstack : systemstack , g : g , g0_sched_sp : g0_sched_sp }
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-11-17 18:29:24 +00:00
callFrameRegs , ret , retaddr := it . advanceRegs ( )
2017-10-05 07:26:19 +00:00
it . frame = it . newStackframe ( ret , retaddr )
2015-10-16 06:42:02 +00:00
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 : ]
}
2017-09-01 13:34:13 +00:00
if it . switchStack ( ) {
2015-10-16 06:42:02 +00:00
return true
}
2017-10-13 20:13:43 +00:00
if it . frame . Ret <= 0 {
it . atend = true
return true
}
2015-10-16 06:42:02 +00:00
it . top = false
it . pc = it . frame . Ret
2017-11-17 18:29:24 +00:00
it . regs = callFrameRegs
2015-10-16 06:42:02 +00:00
return true
}
2017-09-01 13:34:13 +00:00
// asmcgocallSPOffsetSaveSlot is the offset from systemstack.SP where
// (goroutine.SP - StackHi) is saved in runtime.asmcgocall after the stack
// switch happens.
const asmcgocallSPOffsetSaveSlot = 0x28
// switchStack will use the current frame to determine if it's time to
// switch between the system stack and the goroutine stack or vice versa.
// Sets it.atend when the top of the stack is reached.
func ( it * stackIterator ) switchStack ( ) bool {
if it . frame . Current . Fn == nil {
return false
}
switch it . frame . Current . Fn . Name {
case "runtime.asmcgocall" :
if it . top || ! it . systemstack {
return false
}
2017-10-13 20:13:43 +00:00
// This function is called by a goroutine to execute a C function and
// switches from the goroutine stack to the system stack.
// Since we are unwinding the stack from callee to caller we have switch
// from the system stack to the goroutine stack.
2017-09-01 13:34:13 +00:00
off , _ := readIntRaw ( it . mem , uintptr ( it . regs . SP ( ) + asmcgocallSPOffsetSaveSlot ) , int64 ( it . bi . Arch . PtrSize ( ) ) ) // reads "offset of SP from StackHi" from where runtime.asmcgocall saved it
oldsp := it . regs . SP ( )
it . regs . Reg ( it . regs . SPRegNum ) . Uint64Val = uint64 ( int64 ( it . stackhi ) - off )
// runtime.asmcgocall can also be called from inside the system stack,
// in that case no stack switch actually happens
if it . regs . SP ( ) == oldsp {
return false
}
it . systemstack = false
// advances to the next frame in the call stack
it . frame . addrret = uint64 ( int64 ( it . regs . SP ( ) ) + int64 ( it . bi . Arch . PtrSize ( ) ) )
it . frame . Ret , _ = readUintRaw ( it . mem , uintptr ( it . frame . addrret ) , int64 ( it . bi . Arch . PtrSize ( ) ) )
it . pc = it . frame . Ret
it . top = false
return true
case "runtime.cgocallback_gofunc" :
// For a detailed description of how this works read the long comment at
// the start of $GOROOT/src/runtime/cgocall.go and the source code of
// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_amd64.s
//
// When a C functions calls back into go it will eventually call into
// runtime.cgocallback_gofunc which is the function that does the stack
// switch from the system stack back into the goroutine stack
// Since we are going backwards on the stack here we see the transition
// as goroutine stack -> system stack.
if it . top || it . systemstack {
return false
}
if it . g0_sched_sp <= 0 {
return false
}
// entering the system stack
it . regs . Reg ( it . regs . SPRegNum ) . Uint64Val = it . g0_sched_sp
// reads the previous value of g0.sched.sp that runtime.cgocallback_gofunc saved on the stack
it . g0_sched_sp , _ = readUintRaw ( it . mem , uintptr ( it . regs . SP ( ) ) , int64 ( it . bi . Arch . PtrSize ( ) ) )
it . top = false
callFrameRegs , ret , retaddr := it . advanceRegs ( )
frameOnSystemStack := it . newStackframe ( ret , retaddr )
it . pc = frameOnSystemStack . Ret
it . regs = callFrameRegs
it . systemstack = true
return true
case "runtime.goexit" , "runtime.rt0_go" , "runtime.mcall" :
// Look for "top of stack" functions.
it . atend = true
return true
default :
2018-01-04 14:46:51 +00:00
if it . systemstack && it . top && it . g != nil && strings . HasPrefix ( it . frame . Current . Fn . Name , "runtime." ) {
// The runtime switches to the system stack in multiple places.
// This usually happens through a call to runtime.systemstack but there
// are functions that switch to the system stack manually (for example
// runtime.morestack).
// Since we are only interested in printing the system stack for cgo
// calls we switch directly to the goroutine stack if we detect that the
// function at the top of the stack is a runtime function.
it . systemstack = false
it . top = false
it . pc = it . g . PC
it . regs . Reg ( it . regs . SPRegNum ) . Uint64Val = it . g . SP
it . regs . Reg ( it . regs . BPRegNum ) . Uint64Val = it . g . BP
return true
}
2017-09-01 13:34:13 +00:00
return false
}
}
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-08-24 07:46:47 +00:00
// frameBase calculates the frame base pseudo-register for DWARF for fn and
// the current frame.
func ( it * stackIterator ) frameBase ( fn * Function ) int64 {
rdr := it . bi . dwarf . Reader ( )
rdr . Seek ( fn . offset )
e , err := rdr . Next ( )
if err != nil {
return 0
}
2017-11-21 08:50:14 +00:00
fb , _ , _ , _ := it . bi . Location ( e , dwarf . AttrFrameBase , it . pc , it . regs )
2017-08-24 07:46:47 +00:00
return fb
}
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
2017-08-24 07:46:47 +00:00
} else {
it . regs . FrameBase = it . frameBase ( fn )
2015-08-28 20:06:29 +00:00
}
2017-09-01 13:34:13 +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 , SystemStack : it . systemstack }
2017-10-05 07:26:19 +00:00
if ! it . top {
2017-09-01 13:34:13 +00:00
fnname := ""
if r . Current . Fn != nil {
fnname = r . Current . Fn . Name
}
switch fnname {
case "runtime.mstart" , "runtime.systemstack_switch" :
// these frames are inserted by runtime.systemstack and there is no CALL
// instruction to look for at pc - 1
r . Call = r . Current
default :
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
}
r . Call . PC = r . Current . PC
2017-10-05 07:26:19 +00:00
}
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.
2017-11-17 18:29:24 +00:00
func ( it * stackIterator ) advanceRegs ( ) ( callFrameRegs op . DwarfRegisters , ret uint64 , retaddr uint64 ) {
2017-10-05 07:26:19 +00:00
fde , err := it . bi . frameEntries . FDEForPC ( it . pc )
var framectx * frame . FrameContext
if _ , nofde := err . ( * frame . NoFDEForPCError ) ; nofde {
2017-09-01 13:34:13 +00:00
framectx = it . bi . Arch . FixFrameUnwindContext ( nil , it . pc , it . bi )
2017-10-05 07:26:19 +00:00
} else {
2017-09-01 13:34:13 +00:00
framectx = it . bi . Arch . FixFrameUnwindContext ( fde . EstablishFrame ( it . pc ) , it . pc , it . bi )
2017-10-05 07:26:19 +00:00
}
cfareg , err := it . executeFrameRegRule ( 0 , framectx . CFA , 0 )
if cfareg == nil {
it . err = fmt . Errorf ( "CFA becomes undefined at PC %#x" , it . pc )
2017-11-17 18:29:24 +00:00
return op . DwarfRegisters { } , 0 , 0
2017-10-05 07:26:19 +00:00
}
it . regs . CFA = int64 ( cfareg . Uint64Val )
2017-09-01 13:34:13 +00:00
callFrameRegs = op . DwarfRegisters { ByteOrder : it . regs . ByteOrder , PCRegNum : it . regs . PCRegNum , SPRegNum : it . regs . SPRegNum , BPRegNum : it . regs . BPRegNum }
2017-10-05 07:26:19 +00:00
// 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
2017-11-17 18:29:24 +00:00
callFrameRegs . AddReg ( uint64 ( amd64DwarfSPRegNum ) , cfareg )
2017-10-05 07:26:19 +00:00
for i , regRule := range framectx . Regs {
reg , err := it . executeFrameRegRule ( i , regRule , it . regs . CFA )
2017-11-17 18:29:24 +00:00
callFrameRegs . AddReg ( i , reg )
2017-10-05 07:26:19 +00:00
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 )
}
}
2017-11-17 18:29:24 +00:00
return callFrameRegs , ret , retaddr
2017-10-05 07:26:19 +00:00
}
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 :
2017-09-01 13:34:13 +00:00
reg := * it . regs . Reg ( regnum )
return & reg , nil
2017-10-05 07:26:19 +00:00
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 :
2017-08-24 07:46:47 +00:00
v , _ , err := op . ExecuteStackProgram ( it . regs , rule . Expression )
2017-10-05 07:26:19 +00:00
if err != nil {
return nil , err
}
return it . readRegisterAt ( regnum , uint64 ( v ) )
case frame . RuleValExpression :
2017-08-24 07:46:47 +00:00
v , _ , err := op . ExecuteStackProgram ( it . regs , rule . Expression )
2017-10-05 07:26:19 +00:00
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
2017-09-01 13:34:13 +00:00
case frame . RuleFramePointer :
curReg := it . regs . Reg ( rule . Reg )
if curReg == nil {
2017-10-05 07:26:19 +00:00
return nil , nil
}
2017-09-01 13:34:13 +00:00
if curReg . Uint64Val <= uint64 ( cfa ) {
return it . readRegisterAt ( regnum , curReg . Uint64Val )
} else {
newReg := * curReg
return & newReg , nil
}
2017-10-05 07:26:19 +00:00
}
}
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
}