mirror of
https://github.com/golang/go
synced 2024-11-22 21:40:03 -07:00
4211384976
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
215 lines
5.6 KiB
Go
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);
|
|
}
|