1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 // A Frame represents a single frame on a remote call stack.
16 // pc is the PC of the next instruction that will execute in
17 // this frame. For lower frames, this is the instruction
18 // following the CALL instruction.
20 // The runtime.Stktop of the active stack segment
22 // The function this stack frame is in
24 // The path and line of the CALL or current instruction. Note
25 // that this differs slightly from the meaning of Frame.pc.
28 // The inner and outer frames of this frame. outer is filled
33 // newFrame returns the top-most Frame of the given g's thread.
34 func newFrame(g remoteStruct
) (*Frame
, os
.Error
) {
36 err
:= try(func(a aborter
) { f
= aNewFrame(a
, g
) })
40 func aNewFrame(a aborter
, g remoteStruct
) *Frame
{
45 switch g
.field(p
.f
.G
.Status
).(remoteInt
).aGet(a
) {
46 case p
.runtime
.Gidle
, p
.runtime
.Gmoribund
, p
.runtime
.Gdead
:
50 // Find the OS thread for this G
52 // TODO(austin) Ideally, we could look at the G's state and
53 // figure out if it's on an OS thread or not. However, this
54 // is difficult because the state isn't updated atomically
55 // with scheduling changes.
56 for _
, t
:= range p
.proc
.Threads() {
59 // TODO(austin) What to do?
63 if thisg
== g
.addr().base
{
64 // Found this G's OS thread
68 // If this thread crashed, try to recover it
70 pc
= p
.peekUintptr(a
, pc
)
78 if pc
== 0 && sp
== 0 {
79 // G is not mapped to an OS thread. Use the
80 // scheduler's stored PC and SP.
81 sched
:= g
.field(p
.f
.G
.Sched
).(remoteStruct
)
82 pc
= proc
.Word(sched
.field(p
.f
.Gobuf
.Pc
).(remoteUint
).aGet(a
))
83 sp
= proc
.Word(sched
.field(p
.f
.Gobuf
.Sp
).(remoteUint
).aGet(a
))
87 stk
:= g
.field(p
.f
.G
.Stackbase
).(remotePtr
).aGet(a
).(remoteStruct
)
89 return prepareFrame(a
, pc
, sp
, stk
, nil)
92 // prepareFrame creates a Frame from the PC and SP within that frame,
93 // as well as the active stack segment. This function takes care of
94 // traversing stack breaks and unwinding closures.
95 func prepareFrame(a aborter
, pc
, sp proc
.Word
, stk remoteStruct
, inner
*Frame
) *Frame
{
96 // Based on src/pkg/runtime/amd64/traceback.c:traceback
105 for i
:= 0; i
< 100; i
++ {
106 // Traverse segmented stack breaks
107 if p
.sys
.lessstack
!= nil && pc
== proc
.Word(p
.sys
.lessstack
.Value
) {
109 pc
= proc
.Word(stk
.field(p
.f
.Stktop
.Gobuf
).(remoteStruct
).field(p
.f
.Gobuf
.Pc
).(remoteUint
).aGet(a
))
111 sp
= proc
.Word(stk
.field(p
.f
.Stktop
.Gobuf
).(remoteStruct
).field(p
.f
.Gobuf
.Sp
).(remoteUint
).aGet(a
))
112 // Get stk->stackbase
113 stk
= stk
.field(p
.f
.Stktop
.Stackbase
).(remotePtr
).aGet(a
).(remoteStruct
)
117 // Get the PC of the call instruction
119 if !top
&& (p
.sys
.goexit
== nil || pc
!= proc
.Word(p
.sys
.goexit
.Value
)) {
124 path
, line
, fn
= p
.syms
.PCToLine(uint64(callpc
))
130 var buf
= make([]byte, p
.ClosureSize())
131 if _
, err
:= p
.Peek(pc
, buf
); err
!= nil {
134 spdelta
, ok
:= p
.ParseClosure(buf
)
136 sp
+= proc
.Word(spdelta
)
137 pc
= p
.peekUintptr(a
, sp
-proc
.Word(p
.PtrSize()))
144 // Compute frame pointer
146 if fn
.FrameSize
< p
.PtrSize() {
147 fp
= sp
+ proc
.Word(p
.PtrSize())
149 fp
= sp
+ proc
.Word(fn
.FrameSize
)
151 // TODO(austin) To really figure out if we're in the prologue,
152 // we need to disassemble the function and look for the call
153 // to morestack. For now, just special case the entry point.
155 // TODO(austin) What if we're in the call to morestack in the
156 // prologue? Then top == false.
157 if top
&& pc
== proc
.Word(fn
.Entry
) {
158 // We're in the function prologue, before SP
159 // has been adjusted for the frame.
160 fp
-= proc
.Word(fn
.FrameSize
- p
.PtrSize())
163 return &Frame
{pc
, sp
, fp
, stk
, fn
, path
, line
, inner
, nil}
166 // Outer returns the Frame that called this Frame, or nil if this is
167 // the outermost frame.
168 func (f
*Frame
) Outer() (*Frame
, os
.Error
) {
170 err
:= try(func(a aborter
) { fr
= f
.aOuter(a
) })
174 func (f
*Frame
) aOuter(a aborter
) *Frame
{
175 // Is there a cached outer frame
183 if f
.fn
== p
.sys
.newproc
&& f
.fn
== p
.sys
.deferproc
{
184 // TODO(rsc) The compiler inserts two push/pop's
185 // around calls to go and defer. Russ says this
186 // should get fixed in the compiler, but we account
188 sp
+= proc
.Word(2 * p
.PtrSize())
191 pc
:= p
.peekUintptr(a
, f
.fp
-proc
.Word(p
.PtrSize()))
196 // TODO(austin) Register this frame for shoot-down.
198 f
.outer
= prepareFrame(a
, pc
, sp
, f
.stk
, f
)
202 // Inner returns the Frame called by this Frame, or nil if this is the
204 func (f
*Frame
) Inner() *Frame
{ return f
.inner
}
206 func (f
*Frame
) String() string {
208 if f
.pc
> proc
.Word(f
.fn
.Value
) {
209 res
+= fmt
.Sprintf("+%#x", f
.pc
-proc
.Word(f
.fn
.Entry
))
211 return res
+ fmt
.Sprintf(" %s:%d", f
.path
, f
.line
)