7a9389330e
gcc/: * gcc.c (default_compilers): Add entry for ".go". * common.opt: Add -static-libgo as a driver option. * doc/install.texi (Configuration): Mention libgo as an option for --enable-shared. Mention go as an option for --enable-languages. * doc/invoke.texi (Overall Options): Mention .go as a file name suffix. Mention go as a -x option. * doc/frontends.texi (G++ and GCC): Mention Go as a supported language. * doc/sourcebuild.texi (Top Level): Mention libgo. * doc/standards.texi (Standards): Add section on Go language. Move references for other languages into their own section. * doc/contrib.texi (Contributors): Mention that I contributed the Go frontend. gcc/testsuite/: * lib/go.exp: New file. * lib/go-dg.exp: New file. * lib/go-torture.exp: New file. * lib/target-supports.exp (check_compile): Match // Go. From-SVN: r167407
213 lines
5.6 KiB
Go
213 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)
|
|
}
|