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"
|
|
|
|
"fmt"
|
|
|
|
)
|
2015-04-19 22:11:33 +00:00
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
CFA int64
|
|
|
|
Ret uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
// Takes an offset from RSP and returns the address of the
|
2015-07-16 18:07:34 +00:00
|
|
|
// instruction the current function is going to return to.
|
2015-06-12 19:51:23 +00:00
|
|
|
func (thread *Thread) ReturnAddress() (uint64, error) {
|
2015-07-01 03:16:52 +00:00
|
|
|
locations, err := thread.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
|
|
|
}
|
|
|
|
|
2015-07-01 03:16:52 +00:00
|
|
|
// Returns the stack trace for thread.
|
|
|
|
// Note the locations in the array are return addresses not call addresses.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (thread *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
2015-06-17 17:11:57 +00:00
|
|
|
regs, err := thread.Registers()
|
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
|
|
|
}
|
2015-07-01 03:16:52 +00:00
|
|
|
return thread.dbp.stacktrace(regs.PC(), regs.SP(), depth)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 03:16:52 +00:00
|
|
|
// Returns the stack trace for a goroutine.
|
|
|
|
// Note the locations in the array are return addresses not call addresses.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (dbp *Process) GoroutineStacktrace(g *G, depth int) ([]Stackframe, error) {
|
2015-06-17 17:11:57 +00:00
|
|
|
if g.thread != nil {
|
|
|
|
return g.thread.Stacktrace(depth)
|
|
|
|
}
|
2015-06-28 15:00:56 +00:00
|
|
|
locs, err := dbp.stacktrace(g.PC, g.SP, depth)
|
2015-07-01 03:16:52 +00:00
|
|
|
return locs, err
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-05-07 21:55:06 +00:00
|
|
|
type NullAddrError struct{}
|
|
|
|
|
|
|
|
func (n NullAddrError) Error() string {
|
|
|
|
return "NULL address"
|
|
|
|
}
|
|
|
|
|
2015-10-16 06:42:02 +00:00
|
|
|
type StackIterator struct {
|
|
|
|
pc, sp uint64
|
|
|
|
top bool
|
|
|
|
frame Stackframe
|
|
|
|
dbp *Process
|
|
|
|
atend bool
|
|
|
|
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 (it *StackIterator) Next() bool {
|
|
|
|
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 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if it.frame.Current.Fn == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if it.frame.Ret <= 0 {
|
|
|
|
it.atend = true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Look for "top of stack" functions.
|
|
|
|
if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" {
|
|
|
|
it.atend = true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
it.top = false
|
|
|
|
it.pc = it.frame.Ret
|
|
|
|
it.sp = uint64(it.frame.CFA)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *StackIterator) Frame() Stackframe {
|
|
|
|
if it.err != nil {
|
|
|
|
panic(it.err)
|
|
|
|
}
|
|
|
|
return it.frame
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *StackIterator) Err() error {
|
|
|
|
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
|
|
|
|
}
|
2015-09-17 08:42:34 +00:00
|
|
|
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, Ret: binary.LittleEndian.Uint64(data)}
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
func (dbp *Process) stacktrace(pc, sp uint64, depth int) ([]Stackframe, error) {
|
|
|
|
frames := make([]Stackframe, 0, depth+1)
|
2015-10-16 06:42:02 +00:00
|
|
|
it := newStackIterator(dbp, pc, sp)
|
|
|
|
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
|
|
|
}
|