From: Austin Clements Date: Fri, 18 Sep 2009 16:09:40 +0000 (-0700) Subject: Implement remote variables X-Git-Tag: weekly.2009-11-06~542 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=20583b587493250a4a39c60b0c79ae66b9ec953b;p=gostls13.git Implement remote variables R=rsc APPROVED=rsc DELTA=282 (281 added, 0 deleted, 1 changed) OCL=34407 CL=34781 --- diff --git a/usr/austin/ogle/rvalue.go b/usr/austin/ogle/rvalue.go index 1449ed6602..2d95a409dd 100644 --- a/usr/austin/ogle/rvalue.go +++ b/usr/austin/ogle/rvalue.go @@ -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 index 0000000000..17c4baa579 --- /dev/null +++ b/usr/austin/ogle/vars.go @@ -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 ""; +} + +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 ""; +} + +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; +}