]> Cypherpunks repositories - gostls13.git/commitdiff
Implement remote variables
authorAustin Clements <aclements@csail.mit.edu>
Fri, 18 Sep 2009 16:09:40 +0000 (09:09 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Fri, 18 Sep 2009 16:09:40 +0000 (09:09 -0700)
R=rsc
APPROVED=rsc
DELTA=282  (281 added, 0 deleted, 1 changed)
OCL=34407
CL=34781

usr/austin/ogle/rvalue.go
usr/austin/ogle/vars.go [new file with mode: 0644]

index 1449ed6602518a4b84990321df28ca81a69690d6..2d95a409dd693055189bf57d5f9ace0227070cc0 100644 (file)
@@ -21,6 +21,14 @@ func (e RemoteMismatchError) String() string {
        return string(e);
 }
 
+// A ReadOnlyError occurs when attempting to set or assign to a
+// read-only value.
+type ReadOnlyError string
+
+func (e ReadOnlyError) String() string {
+       return string(e);
+}
+
 // A maker is a function that converts a remote address into an
 // interpreter Value.
 type maker func(remote) eval.Value
@@ -351,7 +359,7 @@ func (v remoteString) Set(t *eval.Thread, x string) {
 func (v remoteString) aSet(a aborter, x string) {
        // TODO(austin) This isn't generally possible without the
        // ability to allocate remote memory.
-       a.Abort(RemoteMismatchError("remote strings cannot be assigned to"));
+       a.Abort(ReadOnlyError("remote strings cannot be assigned to"));
 }
 
 func mkString(r remote) eval.Value {
diff --git a/usr/austin/ogle/vars.go b/usr/austin/ogle/vars.go
new file mode 100644 (file)
index 0000000..17c4baa
--- /dev/null
@@ -0,0 +1,277 @@
+// 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;
+}