diff options
Diffstat (limited to 'src/pkg/exp/ogle/frame.go')
-rw-r--r-- | src/pkg/exp/ogle/frame.go | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/pkg/exp/ogle/frame.go b/src/pkg/exp/ogle/frame.go new file mode 100644 index 000000000..bf2b39134 --- /dev/null +++ b/src/pkg/exp/ogle/frame.go @@ -0,0 +1,214 @@ +// 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); +} |