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