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

3
import (
4
	"errors"
5
	"fmt"
D
Derek Parker 已提交
6

D
Derek Parker 已提交
7
	"github.com/derekparker/delve/pkg/dwarf/frame"
8
)
9

A
Alessandro Arzilli 已提交
10 11 12 13 14
// This code is partly adaped from runtime.gentraceback in
// $GOROOT/src/runtime/traceback.go

const runtimeStackBarrier = "runtime.stackBarrier"

D
Derek Parker 已提交
15
// NoReturnAddr is returned when return address
N
Nan Xiao 已提交
16
// could not be found during stack trace.
17 18 19 20 21 22 23 24
type NoReturnAddr struct {
	fn string
}

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

D
Derek Parker 已提交
25
// Stackframe represents a frame in a system stack.
26
type Stackframe struct {
27
	// Address the function above this one on the call stack will return to.
28 29 30
	Current Location
	// Address of the call instruction for the function above on the call stack.
	Call Location
31
	// Start address of the stack frame.
32
	CFA int64
33
	// Description of the stack frame.
34
	FDE *frame.FrameDescriptionEntry
35
	// Return address for this stack frame (as read from the stack frame itself).
36
	Ret uint64
A
Alessandro Arzilli 已提交
37 38
	// Address to the memory location containing the return address
	addrret uint64
39 40
}

41 42 43
// FrameToScope returns a new EvalScope for this frame
func (p *Process) FrameToScope(frame Stackframe) *EvalScope {
	return &EvalScope{frame.Current.PC, frame.CFA, p.currentThread, nil, p.BinInfo()}
44 45
}

D
Derek Parker 已提交
46 47 48 49
// ReturnAddress returns the return address of the function
// this thread is executing.
func (t *Thread) ReturnAddress() (uint64, error) {
	locations, err := t.Stacktrace(2)
50 51 52
	if err != nil {
		return 0, err
	}
53
	if len(locations) < 2 {
54
		return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
55
	}
56
	return locations[1].Current.PC, nil
A
aarzilli 已提交
57 58
}

A
Alessandro Arzilli 已提交
59
func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, error) {
A
aarzilli 已提交
60
	regs, err := t.Registers(false)
A
aarzilli 已提交
61 62 63
	if err != nil {
		return nil, err
	}
64
	return newStackIterator(&t.dbp.bi, t, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
A
aarzilli 已提交
65 66
}

D
Derek Parker 已提交
67
// Stacktrace returns the stack trace for thread.
D
Derek Parker 已提交
68
// Note the locations in the array are return addresses not call addresses.
D
Derek Parker 已提交
69
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
A
Alessandro Arzilli 已提交
70
	it, err := t.stackIterator(nil, -1)
71
	if err != nil {
D
Derek Parker 已提交
72
		return nil, err
73
	}
A
aarzilli 已提交
74
	return it.stacktrace(depth)
A
aarzilli 已提交
75 76
}

A
aarzilli 已提交
77
func (g *G) stackIterator() (*stackIterator, error) {
A
Alessandro Arzilli 已提交
78 79 80 81
	stkbar, err := g.stkbar()
	if err != nil {
		return nil, err
	}
A
aarzilli 已提交
82
	if g.thread != nil {
A
Alessandro Arzilli 已提交
83
		return g.thread.stackIterator(stkbar, g.stkbarPos)
A
aarzilli 已提交
84
	}
85
	return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
A
aarzilli 已提交
86 87 88 89 90 91 92 93
}

// 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
A
aarzilli 已提交
94
	}
A
aarzilli 已提交
95
	return it.stacktrace(depth)
A
aarzilli 已提交
96 97
}

D
Derek Parker 已提交
98 99
// GoroutineLocation returns the location of the given
// goroutine.
D
Derek Parker 已提交
100
func (dbp *Process) GoroutineLocation(g *G) *Location {
101
	f, l, fn := dbp.bi.PCToLine(g.PC)
A
aarzilli 已提交
102
	return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
103 104
}

D
Derek Parker 已提交
105
// NullAddrError is an error for a null address.
106 107 108 109 110 111
type NullAddrError struct{}

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

A
aarzilli 已提交
112
// stackIterator holds information
D
Derek Parker 已提交
113 114
// required to iterate and walk the program
// stack.
A
aarzilli 已提交
115
type stackIterator struct {
116 117 118 119
	pc, sp, bp uint64
	top        bool
	atend      bool
	frame      Stackframe
120 121
	bi         *BinaryInfo
	mem        memoryReadWriter
122
	err        error
123

A
Alessandro Arzilli 已提交
124 125 126 127 128 129 130 131 132
	stackBarrierPC uint64
	stkbar         []savedLR
}

type savedLR struct {
	ptr uint64
	val uint64
}

133 134
func newStackIterator(bi *BinaryInfo, mem memoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
	stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
135 136 137
	var stackBarrierPC uint64
	if stackBarrierFunc != nil && stkbar != nil {
		stackBarrierPC = stackBarrierFunc.Entry
138
		fn := bi.goSymTable.PCToFunc(pc)
A
Alessandro Arzilli 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152
		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:]
	}
153
	return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
154 155
}

D
Derek Parker 已提交
156
// Next points the iterator to the next stack frame.
A
aarzilli 已提交
157
func (it *stackIterator) Next() bool {
158 159 160
	if it.err != nil || it.atend {
		return false
	}
161
	it.frame, it.err = it.frameInfo(it.pc, it.sp, it.bp, it.top)
162
	if it.err != nil {
163
		if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
A
Alessandro Arzilli 已提交
164
			it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
165 166 167 168
			it.atend = true
			it.err = nil
			return true
		}
169 170 171 172 173 174 175
		return false
	}

	if it.frame.Ret <= 0 {
		it.atend = true
		return true
	}
A
Alessandro Arzilli 已提交
176 177 178 179 180 181 182

	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:]
	}

183
	// Look for "top of stack" functions.
184
	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") {
185 186 187 188 189 190 191
		it.atend = true
		return true
	}

	it.top = false
	it.pc = it.frame.Ret
	it.sp = uint64(it.frame.CFA)
192
	it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.arch.PtrSize()))
193 194 195
	return true
}

D
Derek Parker 已提交
196
// Frame returns the frame the iterator is pointing at.
A
aarzilli 已提交
197
func (it *stackIterator) Frame() Stackframe {
198 199 200 201 202 203
	if it.err != nil {
		panic(it.err)
	}
	return it.frame
}

D
Derek Parker 已提交
204
// Err returns the error encountered during stack iteration.
A
aarzilli 已提交
205
func (it *stackIterator) Err() error {
206 207 208
	return it.err
}

209 210
func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
	fde, err := it.bi.frameEntries.FDEForPC(pc)
211 212 213 214 215
	if _, nofde := err.(*frame.NoFDEForPCError); nofde {
		if bp == 0 {
			return Stackframe{}, err
		}
		// When no FDE is available attempt to use BP instead
216 217 218
		retaddr := uintptr(int(bp) + it.bi.arch.PtrSize())
		cfa := int64(retaddr) + int64(it.bi.arch.PtrSize())
		return it.newStackframe(pc, cfa, retaddr, nil, top)
219
	}
220

221 222 223 224
	spoffset, retoffset := fde.ReturnAddressOffset(pc)
	cfa := int64(sp) + spoffset

	retaddr := uintptr(cfa + retoffset)
225
	return it.newStackframe(pc, cfa, retaddr, fde, top)
226 227
}

228
func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) {
229 230 231
	if retaddr == 0 {
		return Stackframe{}, NullAddrError{}
	}
232 233
	f, l, fn := it.bi.PCToLine(pc)
	ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.arch.PtrSize()))
234 235 236
	if err != nil {
		return Stackframe{}, err
	}
237
	r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
238
	if !top {
239
		r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
240
		r.Call.PC = r.Current.PC
241 242 243 244
	} else {
		r.Call = r.Current
	}
	return r, nil
245 246
}

A
aarzilli 已提交
247
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
248 249 250
	if depth < 0 {
		return nil, errors.New("negative maximum stack depth")
	}
251
	frames := make([]Stackframe, 0, depth+1)
252 253 254
	for it.Next() {
		frames = append(frames, it.Frame())
		if len(frames) >= depth+1 {
A
aarzilli 已提交
255 256
			break
		}
257 258 259
	}
	if err := it.Err(); err != nil {
		return nil, err
260
	}
261
	return frames, nil
262
}