1
0
mirror of https://github.com/golang/go synced 2024-11-26 00:28:00 -07:00
go/usr/austin/ogle/vars.go
Austin Clements 20583b5874 Implement remote variables
R=rsc
APPROVED=rsc
DELTA=282  (281 added, 0 deleted, 1 changed)
OCL=34407
CL=34781
2009-09-18 09:09:40 -07:00

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