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

3 4
import (
	"encoding/binary"
5
	"errors"
6
	"fmt"
D
Derek Parker 已提交
7

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

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

const runtimeStackBarrier = "runtime.stackBarrier"

D
Derek Parker 已提交
16
// NoReturnAddr is returned when return address
N
Nan Xiao 已提交
17
// could not be found during stack trace.
18 19 20 21 22 23 24 25
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 已提交
26
// Stackframe represents a frame in a system stack.
27
type Stackframe struct {
28
	// Address the function above this one on the call stack will return to.
29 30 31
	Current Location
	// Address of the call instruction for the function above on the call stack.
	Call Location
32
	// Start address of the stack frame.
33
	CFA int64
34
	// Description of the stack frame.
35
	FDE *frame.FrameDescriptionEntry
36
	// Return address for this stack frame (as read from the stack frame itself).
37
	Ret uint64
A
Alessandro Arzilli 已提交
38 39
	// Address to the memory location containing the return address
	addrret uint64
40 41
}

D
Derek Parker 已提交
42
// Scope returns a new EvalScope using this frame.
43 44
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
	return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
45 46
}

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

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

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

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

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

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

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

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

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

A
Alessandro Arzilli 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	stackBarrierPC uint64
	stkbar         []savedLR
}

type savedLR struct {
	ptr uint64
	val uint64
}

func newStackIterator(dbp *Process, pc, sp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
	stackBarrierPC := dbp.goSymTable.LookupFunc(runtimeStackBarrier).Entry
	if stkbar != nil {
		fn := dbp.goSymTable.PCToFunc(pc)
		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:]
	}
	return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
152 153
}

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

	if it.frame.Current.Fn == nil {
171 172 173
		if it.top {
			it.err = fmt.Errorf("PC not associated to any function")
		}
174 175 176 177 178 179 180
		return false
	}

	if it.frame.Ret <= 0 {
		it.atend = true
		return true
	}
A
Alessandro Arzilli 已提交
181 182 183 184 185 186 187

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

188
	// Look for "top of stack" functions.
189
	if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall" {
190 191 192 193 194 195 196 197 198 199
		it.atend = true
		return true
	}

	it.top = false
	it.pc = it.frame.Ret
	it.sp = uint64(it.frame.CFA)
	return true
}

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

D
Derek Parker 已提交
208
// Err returns the error encountered during stack iteration.
A
aarzilli 已提交
209
func (it *stackIterator) Err() error {
210 211 212
	return it.err
}

213
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
D
Derek Parker 已提交
214
	f, l, fn := dbp.PCToLine(pc)
215 216 217 218 219 220 221 222 223 224 225
	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{}
	}
D
Derek Parker 已提交
226
	data, err := dbp.currentThread.readMemory(retaddr, dbp.arch.PtrSize())
227 228 229
	if err != nil {
		return Stackframe{}, err
	}
A
Alessandro Arzilli 已提交
230
	r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: binary.LittleEndian.Uint64(data), addrret: uint64(retaddr)}
231 232 233 234 235 236 237
	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
238 239
}

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