1
0
mirror of https://github.com/golang/go synced 2024-11-26 05:17:58 -07:00
go/usr/austin/ogle/frame.go
Austin Clements 4211384976 Switch ogle to in-tree gosym package. Delete my private sym
package.  If a Sym is a function symbol, include a reference
to the Func so it's easily accessible when you're traversing
the list of all symbols.  This diff is more interesting than
the proc switch because the gosym interface differs from the
old sym interface.

R=rsc
APPROVED=rsc
DELTA=1957  (34 added, 1868 deleted, 55 changed)
OCL=34969
CL=35008
2009-09-25 09:39:08 -07:00

215 lines
5.6 KiB
Go

// 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);
}