summaryrefslogtreecommitdiff
path: root/libgo/go/exp/ogle/vars.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/ogle/vars.go')
-rw-r--r--libgo/go/exp/ogle/vars.go272
1 files changed, 272 insertions, 0 deletions
diff --git a/libgo/go/exp/ogle/vars.go b/libgo/go/exp/ogle/vars.go
new file mode 100644
index 000000000..8a3a14791
--- /dev/null
+++ b/libgo/go/exp/ogle/vars.go
@@ -0,0 +1,272 @@
+// 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 (
+ "debug/gosym"
+ "debug/proc"
+ "exp/eval"
+ "log"
+ "os"
+)
+
+/*
+ * 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 *gosym.Func
+ 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 *gosym.Func
+ 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("fail")
+}
+
+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 {
+ if s.ReceiverName() != "" {
+ // TODO(austin)
+ continue
+ }
+
+ // Package
+ pkgName := s.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 := s.BaseName()
+ if _, ok := pkg[name]; ok {
+ log.Printf("Multiple definitions of symbol %s", s.Name)
+ continue
+ }
+
+ // Symbol type
+ rt, err := p.typeOfSym(&s)
+ if err != nil {
+ return err
+ }
+
+ // Definition
+ switch s.Type {
+ case 'D', 'd', 'B', 'b':
+ // Global variable
+ if rt == nil {
+ continue
+ }
+ pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})}
+
+ case 'T', 't', 'L', 'l':
+ // Function
+ s := s.Func
+ // 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.Printf("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 *gosym.Sym) (*remoteType, os.Error) {
+ if s.GoType == 0 {
+ return nil, nil
+ }
+ addr := proc.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 *gosym.Func) (*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)
+ 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)
+ 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
+}