Merge from mainline (167278:168000).
[official-gcc/graphite-test-results.git] / libgo / go / exp / ogle / frame.go
blob1538362bad233b7c28e55d742a09a941f367567a
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.
5 package ogle
7 import (
8 "debug/gosym"
9 "debug/proc"
10 "fmt"
11 "os"
14 // A Frame represents a single frame on a remote call stack.
15 type Frame struct {
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.
19 pc, sp, fp proc.Word
20 // The runtime.Stktop of the active stack segment
21 stk remoteStruct
22 // The function this stack frame is in
23 fn *gosym.Func
24 // The path and line of the CALL or current instruction. Note
25 // that this differs slightly from the meaning of Frame.pc.
26 path string
27 line int
28 // The inner and outer frames of this frame. outer is filled
29 // in lazily.
30 inner, outer *Frame
33 // newFrame returns the top-most Frame of the given g's thread.
34 func newFrame(g remoteStruct) (*Frame, os.Error) {
35 var f *Frame
36 err := try(func(a aborter) { f = aNewFrame(a, g) })
37 return f, err
40 func aNewFrame(a aborter, g remoteStruct) *Frame {
41 p := g.r.p
42 var pc, sp proc.Word
44 // Is this G alive?
45 switch g.field(p.f.G.Status).(remoteInt).aGet(a) {
46 case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead:
47 return nil
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() {
57 regs, err := t.Regs()
58 if err != nil {
59 // TODO(austin) What to do?
60 continue
62 thisg := p.G(regs)
63 if thisg == g.addr().base {
64 // Found this G's OS thread
65 pc = regs.PC()
66 sp = regs.SP()
68 // If this thread crashed, try to recover it
69 if pc == 0 {
70 pc = p.peekUintptr(a, pc)
71 sp += 8
74 break
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))
86 // Get Stktop
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
97 p := stk.r.p
98 top := inner == nil
100 // Get function
101 var path string
102 var line int
103 var fn *gosym.Func
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) {
108 // Get stk->gobuf.pc
109 pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a))
110 // Get stk->gobuf.sp
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)
114 continue
117 // Get the PC of the call instruction
118 callpc := pc
119 if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) {
120 callpc--
123 // Look up function
124 path, line, fn = p.syms.PCToLine(uint64(callpc))
125 if fn != nil {
126 break
129 // Closure?
130 var buf = make([]byte, p.ClosureSize())
131 if _, err := p.Peek(pc, buf); err != nil {
132 break
134 spdelta, ok := p.ParseClosure(buf)
135 if ok {
136 sp += proc.Word(spdelta)
137 pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize()))
140 if fn == nil {
141 return nil
144 // Compute frame pointer
145 var fp proc.Word
146 if fn.FrameSize < p.PtrSize() {
147 fp = sp + proc.Word(p.PtrSize())
148 } else {
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) {
169 var fr *Frame
170 err := try(func(a aborter) { fr = f.aOuter(a) })
171 return fr, err
174 func (f *Frame) aOuter(a aborter) *Frame {
175 // Is there a cached outer frame
176 if f.outer != nil {
177 return f.outer
180 p := f.stk.r.p
182 sp := f.fp
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
187 // for it for now.
188 sp += proc.Word(2 * p.PtrSize())
191 pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize()))
192 if pc < 0x1000 {
193 return nil
196 // TODO(austin) Register this frame for shoot-down.
198 f.outer = prepareFrame(a, pc, sp, f.stk, f)
199 return f.outer
202 // Inner returns the Frame called by this Frame, or nil if this is the
203 // innermost frame.
204 func (f *Frame) Inner() *Frame { return f.inner }
206 func (f *Frame) String() string {
207 res := f.fn.Name
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)