stack.go 3.1 KB
Newer Older
D
Derek Parker 已提交
1
package proc
2

3 4 5 6
import (
	"encoding/binary"
	"fmt"
)
7

8 9 10 11 12 13 14 15
type NoReturnAddr struct {
	fn string
}

func (nra NoReturnAddr) Error() string {
	return fmt.Sprintf("could not find return address for %s", nra.fn)
}

16
type Stackframe struct {
17
	// Address the function above this one on the call stack will return to.
18 19 20 21 22 23 24 25 26
	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}
27 28
}

29
// Takes an offset from RSP and returns the address of the
D
Derek Parker 已提交
30
// instruction the current function is going to return to.
D
Derek Parker 已提交
31
func (thread *Thread) ReturnAddress() (uint64, error) {
D
Derek Parker 已提交
32
	locations, err := thread.Stacktrace(2)
33 34 35
	if err != nil {
		return 0, err
	}
36
	if len(locations) < 2 {
37
		return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
38
	}
39
	return locations[1].Current.PC, nil
A
aarzilli 已提交
40 41
}

D
Derek Parker 已提交
42 43
// Returns the stack trace for thread.
// Note the locations in the array are return addresses not call addresses.
44
func (thread *Thread) Stacktrace(depth int) ([]Stackframe, error) {
A
aarzilli 已提交
45
	regs, err := thread.Registers()
46
	if err != nil {
D
Derek Parker 已提交
47
		return nil, err
48
	}
D
Derek Parker 已提交
49
	return thread.dbp.stacktrace(regs.PC(), regs.SP(), depth)
A
aarzilli 已提交
50 51
}

D
Derek Parker 已提交
52 53
// Returns the stack trace for a goroutine.
// Note the locations in the array are return addresses not call addresses.
54
func (dbp *Process) GoroutineStacktrace(g *G, depth int) ([]Stackframe, error) {
A
aarzilli 已提交
55 56 57
	if g.thread != nil {
		return g.thread.Stacktrace(depth)
	}
A
aarzilli 已提交
58
	locs, err := dbp.stacktrace(g.PC, g.SP, depth)
D
Derek Parker 已提交
59
	return locs, err
A
aarzilli 已提交
60 61
}

D
Derek Parker 已提交
62
func (dbp *Process) GoroutineLocation(g *G) *Location {
A
aarzilli 已提交
63 64
	f, l, fn := dbp.PCToLine(g.PC)
	return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
65 66
}

67 68 69 70 71 72
type NullAddrError struct{}

func (n NullAddrError) Error() string {
	return "NULL address"
}

73
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
D
Derek Parker 已提交
74
	f, l, fn := dbp.PCToLine(pc)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
	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
	}
90 91 92 93 94 95 96 97
	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
98 99 100 101 102 103
}

func (dbp *Process) stacktrace(pc, sp uint64, depth int) ([]Stackframe, error) {
	frames := make([]Stackframe, 0, depth+1)

	for i := 0; i < depth+1; i++ {
104
		frame, err := dbp.frameInfo(pc, sp, i == 0)
105 106 107
		if err != nil {
			return nil, err
		}
108
		if frame.Current.Fn == nil {
A
aarzilli 已提交
109 110
			break
		}
111 112
		frames = append(frames, frame)
		if frame.Ret <= 0 {
D
Derek Parker 已提交
113 114
			break
		}
115
		// Look for "top of stack" functions.
116
		if frame.Current.Fn.Name == "runtime.goexit" || frame.Current.Fn.Name == "runtime.rt0_go" {
A
aarzilli 已提交
117 118
			break
		}
119 120 121

		pc = frame.Ret
		sp = uint64(frame.CFA)
122
	}
123
	return frames, nil
124
}