mirror of
https://github.com/golang/go
synced 2024-11-26 02:27:56 -07:00
278 lines
6.9 KiB
Go
278 lines
6.9 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 (
|
||
|
"eval";
|
||
|
"log";
|
||
|
"os";
|
||
|
"ptrace";
|
||
|
"sym";
|
||
|
)
|
||
|
|
||
|
/*
|
||
|
* Remote frame pointers
|
||
|
*/
|
||
|
|
||
|
// A NotOnStack error occurs when attempting to access a variable in a
|
||
|
// remote frame where that remote frame is not on the current stack.
|
||
|
type NotOnStack struct {
|
||
|
Fn *sym.TextSym;
|
||
|
Goroutine *Goroutine;
|
||
|
}
|
||
|
|
||
|
func (e NotOnStack) String() string {
|
||
|
return "function " + e.Fn.Name + " not on " + e.Goroutine.String() + "'s stack";
|
||
|
}
|
||
|
|
||
|
// A remoteFramePtr is an implementation of eval.PtrValue that
|
||
|
// represents a pointer to a function frame in a remote process. When
|
||
|
// accessed, this locates the function on the current goroutine's
|
||
|
// stack and returns a structure containing the local variables of
|
||
|
// that function.
|
||
|
type remoteFramePtr struct {
|
||
|
p *Process;
|
||
|
fn *sym.TextSym;
|
||
|
rt *remoteType;
|
||
|
}
|
||
|
|
||
|
func (v remoteFramePtr) String() string {
|
||
|
// TODO(austin): This could be a really awesome string method
|
||
|
return "<remote frame>";
|
||
|
}
|
||
|
|
||
|
func (v remoteFramePtr) Assign(t *eval.Thread, o eval.Value) {
|
||
|
v.Set(t, o.(eval.PtrValue).Get(t));
|
||
|
}
|
||
|
|
||
|
func (v remoteFramePtr) Get(t *eval.Thread) eval.Value {
|
||
|
g := v.p.curGoroutine;
|
||
|
if g == nil || g.frame == nil {
|
||
|
t.Abort(NoCurrentGoroutine{});
|
||
|
}
|
||
|
|
||
|
for f := g.frame; f != nil; f = f.aOuter(t) {
|
||
|
if f.fn != v.fn {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// TODO(austin): Register for shootdown with f
|
||
|
return v.rt.mk(remote{f.fp, v.p});
|
||
|
}
|
||
|
|
||
|
t.Abort(NotOnStack{v.fn, g});
|
||
|
panic();
|
||
|
}
|
||
|
|
||
|
func (v remoteFramePtr) Set(t *eval.Thread, x eval.Value) {
|
||
|
// Theoretically this could be a static error. If remote
|
||
|
// packages were packages, remote frames could just be defined
|
||
|
// as constants.
|
||
|
t.Abort(ReadOnlyError("remote frames cannot be assigned to"));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Remote packages
|
||
|
*/
|
||
|
|
||
|
// TODO(austin): Remote packages are implemented as structs right now,
|
||
|
// which has some weird consequences. You can attempt to assign to a
|
||
|
// remote package. It also produces terrible error messages.
|
||
|
// Ideally, these would actually be packages, but somehow first-class
|
||
|
// so they could be assigned to other names.
|
||
|
|
||
|
// A remotePackage is an implementation of eval.StructValue that
|
||
|
// represents a package in a remote process. It's essentially a
|
||
|
// regular struct, except it cannot be assigned to.
|
||
|
type remotePackage struct {
|
||
|
defs []eval.Value;
|
||
|
}
|
||
|
|
||
|
func (v remotePackage) String() string {
|
||
|
return "<remote package>";
|
||
|
}
|
||
|
|
||
|
func (v remotePackage) Assign(t *eval.Thread, o eval.Value) {
|
||
|
t.Abort(ReadOnlyError("remote packages cannot be assigned to"));
|
||
|
}
|
||
|
|
||
|
func (v remotePackage) Get(t *eval.Thread) eval.StructValue {
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
func (v remotePackage) Field(t *eval.Thread, i int) eval.Value {
|
||
|
return v.defs[i];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Remote variables
|
||
|
*/
|
||
|
|
||
|
// populateWorld defines constants in the given world for each package
|
||
|
// in this process. These packages are structs that, in turn, contain
|
||
|
// fields for each global and function in that package.
|
||
|
func (p *Process) populateWorld(w *eval.World) os.Error {
|
||
|
type def struct {
|
||
|
t eval.Type;
|
||
|
v eval.Value;
|
||
|
}
|
||
|
packages := make(map[string] map[string] def);
|
||
|
|
||
|
for _, s := range p.syms.Syms {
|
||
|
sc := s.Common();
|
||
|
if sc.ReceiverName() != "" {
|
||
|
// TODO(austin)
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Package
|
||
|
pkgName := sc.PackageName();
|
||
|
switch pkgName {
|
||
|
case "", "type", "extratype", "string", "go":
|
||
|
// "go" is really "go.string"
|
||
|
continue;
|
||
|
}
|
||
|
pkg, ok := packages[pkgName];
|
||
|
if !ok {
|
||
|
pkg = make(map[string] def);
|
||
|
packages[pkgName] = pkg;
|
||
|
}
|
||
|
|
||
|
// Symbol name
|
||
|
name := sc.BaseName();
|
||
|
if prev, ok := pkg[name]; ok {
|
||
|
log.Stderrf("Multiple definitions of symbol %s", sc.Name);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Symbol type
|
||
|
rt, err := p.typeOfSym(sc);
|
||
|
if err != nil {
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
// Definition
|
||
|
switch sc.Type {
|
||
|
case 'D', 'd', 'B', 'b':
|
||
|
// Global variable
|
||
|
if rt == nil {
|
||
|
continue;
|
||
|
}
|
||
|
pkg[name] = def{rt.Type, rt.mk(remote{ptrace.Word(sc.Value), p})};
|
||
|
|
||
|
case 'T', 't', 'L', 'l':
|
||
|
// Function
|
||
|
s := s.(*sym.TextSym);
|
||
|
// TODO(austin): Ideally, this would *also* be
|
||
|
// callable. How does that interact with type
|
||
|
// conversion syntax?
|
||
|
rt, err := p.makeFrameType(s);
|
||
|
if err != nil {
|
||
|
return err;
|
||
|
}
|
||
|
pkg[name] = def{eval.NewPtrType(rt.Type), remoteFramePtr{p, s, rt}};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO(austin): Define remote types
|
||
|
|
||
|
// Define packages
|
||
|
for pkgName, defs := range packages {
|
||
|
fields := make([]eval.StructField, len(defs));
|
||
|
vals := make([]eval.Value, len(defs));
|
||
|
i := 0;
|
||
|
for name, def := range defs {
|
||
|
fields[i].Name = name;
|
||
|
fields[i].Type = def.t;
|
||
|
vals[i] = def.v;
|
||
|
i++;
|
||
|
}
|
||
|
pkgType := eval.NewStructType(fields);
|
||
|
pkgVal := remotePackage{vals};
|
||
|
|
||
|
err := w.DefineConst(pkgName, pkgType, pkgVal);
|
||
|
if err != nil {
|
||
|
log.Stderrf("while defining package %s: %v", pkgName, err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
// typeOfSym returns the type associated with a symbol. If the symbol
|
||
|
// has no type, returns nil.
|
||
|
func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) {
|
||
|
if s.GoType == 0 {
|
||
|
return nil, nil;
|
||
|
}
|
||
|
addr := ptrace.Word(s.GoType);
|
||
|
var rt *remoteType;
|
||
|
err := try(func(a aborter) {
|
||
|
rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct));
|
||
|
});
|
||
|
if err != nil {
|
||
|
return nil, err;
|
||
|
}
|
||
|
return rt, nil;
|
||
|
}
|
||
|
|
||
|
// makeFrameType constructs a struct type for the frame of a function.
|
||
|
// The offsets in this struct type are such that the struct can be
|
||
|
// instantiated at this function's frame pointer.
|
||
|
func (p *Process) makeFrameType(s *sym.TextSym) (*remoteType, os.Error) {
|
||
|
n := len(s.Params) + len(s.Locals);
|
||
|
fields := make([]eval.StructField, n);
|
||
|
layout := make([]remoteStructField, n);
|
||
|
i := 0;
|
||
|
|
||
|
// TODO(austin): There can be multiple locals/parameters with
|
||
|
// the same name. We probably need liveness information to do
|
||
|
// anything about this. Once we have that, perhaps we give
|
||
|
// such fields interface{} type? Or perhaps we disambiguate
|
||
|
// the names with numbers. Disambiguation is annoying for
|
||
|
// things like "i", where there's an obvious right answer.
|
||
|
|
||
|
for _, param := range s.Params {
|
||
|
rt, err := p.typeOfSym(param.Common());
|
||
|
if err != nil {
|
||
|
return nil, err;
|
||
|
}
|
||
|
if rt == nil {
|
||
|
//fmt.Printf(" (no type)\n");
|
||
|
continue;
|
||
|
}
|
||
|
// TODO(austin): Why do local variables carry their
|
||
|
// package name?
|
||
|
fields[i].Name = param.BaseName();
|
||
|
fields[i].Type = rt.Type;
|
||
|
// Parameters have positive offsets from FP
|
||
|
layout[i].offset = int(param.Value);
|
||
|
layout[i].fieldType = rt;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
for _, local := range s.Locals {
|
||
|
rt, err := p.typeOfSym(local.Common());
|
||
|
if err != nil {
|
||
|
return nil, err;
|
||
|
}
|
||
|
if rt == nil {
|
||
|
continue;
|
||
|
}
|
||
|
fields[i].Name = local.BaseName();
|
||
|
fields[i].Type = rt.Type;
|
||
|
// Locals have negative offsets from FP - PtrSize
|
||
|
layout[i].offset = -int(local.Value) - p.PtrSize();
|
||
|
layout[i].fieldType = rt;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
fields = fields[0:i];
|
||
|
layout = layout[0:i];
|
||
|
t := eval.NewStructType(fields);
|
||
|
mk := func(r remote) eval.Value { return remoteStruct{r, layout} };
|
||
|
return &remoteType{t, 0, 0, mk}, nil;
|
||
|
}
|