--- /dev/null
+// 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;
+}