1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ogle
import (
"debug/gosym"
"debug/proc"
"fmt"
"os"
)
// A Frame represents a single frame on a remote call stack.
type Frame struct {
// pc is the PC of the next instruction that will execute in
// this frame. For lower frames, this is the instruction
// following the CALL instruction.
pc, sp, fp proc.Word
// The runtime.Stktop of the active stack segment
stk remoteStruct
// The function this stack frame is in
fn *gosym.Func
// The path and line of the CALL or current instruction. Note
// that this differs slightly from the meaning of Frame.pc.
path string
line int
// The inner and outer frames of this frame. outer is filled
// in lazily.
inner, outer *Frame
}
// newFrame returns the top-most Frame of the given g's thread.
func newFrame(g remoteStruct) (*Frame, os.Error) {
var f *Frame
err := try(func(a aborter) { f = aNewFrame(a, g) })
return f, err
}
func aNewFrame(a aborter, g remoteStruct) *Frame {
p := g.r.p
var pc, sp proc.Word
// Is this G alive?
switch g.field(p.f.G.Status).(remoteInt).aGet(a) {
case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead:
return nil
}
// Find the OS thread for this G
// TODO(austin) Ideally, we could look at the G's state and
// figure out if it's on an OS thread or not. However, this
// is difficult because the state isn't updated atomically
// with scheduling changes.
for _, t := range p.proc.Threads() {
regs, err := t.Regs()
if err != nil {
// TODO(austin) What to do?
continue
}
thisg := p.G(regs)
if thisg == g.addr().base {
// Found this G's OS thread
pc = regs.PC()
sp = regs.SP()
// If this thread crashed, try to recover it
if pc == 0 {
pc = p.peekUintptr(a, pc)
sp += 8
}
break
}
}
if pc == 0 && sp == 0 {
// G is not mapped to an OS thread. Use the
// scheduler's stored PC and SP.
sched := g.field(p.f.G.Sched).(remoteStruct)
pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a))
sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a))
}
// Get Stktop
stk := g.field(p.f.G.Stackbase).(remotePtr).aGet(a).(remoteStruct)
return prepareFrame(a, pc, sp, stk, nil)
}
// prepareFrame creates a Frame from the PC and SP within that frame,
// as well as the active stack segment. This function takes care of
// traversing stack breaks and unwinding closures.
func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame {
// Based on src/pkg/runtime/amd64/traceback.c:traceback
p := stk.r.p
top := inner == nil
// Get function
var path string
var line int
var fn *gosym.Func
for i := 0; i < 100; i++ {
// Traverse segmented stack breaks
if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) {
// Get stk->gobuf.pc
pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a))
// Get stk->gobuf.sp
sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a))
// Get stk->stackbase
stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct)
continue
}
// Get the PC of the call instruction
callpc := pc
if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) {
callpc--
}
// Look up function
path, line, fn = p.syms.PCToLine(uint64(callpc))
if fn != nil {
break
}
// Closure?
var buf = make([]byte, p.ClosureSize())
if _, err := p.Peek(pc, buf); err != nil {
break
}
spdelta, ok := p.ParseClosure(buf)
if ok {
sp += proc.Word(spdelta)
pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize()))
}
}
if fn == nil {
return nil
}
// Compute frame pointer
var fp proc.Word
if fn.FrameSize < p.PtrSize() {
fp = sp + proc.Word(p.PtrSize())
} else {
fp = sp + proc.Word(fn.FrameSize)
}
// TODO(austin) To really figure out if we're in the prologue,
// we need to disassemble the function and look for the call
// to morestack. For now, just special case the entry point.
//
// TODO(austin) What if we're in the call to morestack in the
// prologue? Then top == false.
if top && pc == proc.Word(fn.Entry) {
// We're in the function prologue, before SP
// has been adjusted for the frame.
fp -= proc.Word(fn.FrameSize - p.PtrSize())
}
return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil}
}
// Outer returns the Frame that called this Frame, or nil if this is
// the outermost frame.
func (f *Frame) Outer() (*Frame, os.Error) {
var fr *Frame
err := try(func(a aborter) { fr = f.aOuter(a) })
return fr, err
}
func (f *Frame) aOuter(a aborter) *Frame {
// Is there a cached outer frame
if f.outer != nil {
return f.outer
}
p := f.stk.r.p
sp := f.fp
if f.fn == p.sys.newproc && f.fn == p.sys.deferproc {
// TODO(rsc) The compiler inserts two push/pop's
// around calls to go and defer. Russ says this
// should get fixed in the compiler, but we account
// for it for now.
sp += proc.Word(2 * p.PtrSize())
}
pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize()))
if pc < 0x1000 {
return nil
}
// TODO(austin) Register this frame for shoot-down.
f.outer = prepareFrame(a, pc, sp, f.stk, f)
return f.outer
}
// Inner returns the Frame called by this Frame, or nil if this is the
// innermost frame.
func (f *Frame) Inner() *Frame { return f.inner }
func (f *Frame) String() string {
res := f.fn.Name
if f.pc > proc.Word(f.fn.Value) {
res += fmt.Sprintf("+%#x", f.pc-proc.Word(f.fn.Entry))
}
return res + fmt.Sprintf(" %s:%d", f.path, f.line)
}
|