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