func (f *Frame) String() string { res := f.fn.Name if f.pc > proc.Word(f.fn.Value) { res += fmt.Sprintf("+%#x", f.pc-proc.Word(f.fn.Entry)) } return res + fmt.Sprintf(" %s:%d", f.path, f.line) }
func (f *Frame) aOuter(a aborter) *Frame { // Is there a cached outer frame if f.outer != nil { return f.outer } p := f.stk.r.p sp := f.fp if f.fn == p.sys.newproc && f.fn == p.sys.deferproc { // TODO(rsc) The compiler inserts two push/pop's // around calls to go and defer. Russ says this // should get fixed in the compiler, but we account // for it for now. sp += proc.Word(2 * p.PtrSize()) } pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize())) if pc < 0x1000 { return nil } // TODO(austin) Register this frame for shoot-down. f.outer = prepareFrame(a, pc, sp, f.stk, f) return f.outer }
// NewProcess constructs a new remote process around a traced // process, an architecture, and a symbol table. func NewProcess(tproc proc.Process, arch Arch, syms *gosym.Table) (*Process, os.Error) { p := &Process{ Arch: arch, proc: tproc, syms: syms, types: make(map[proc.Word]*remoteType), breakpointHooks: make(map[proc.Word]*breakpointHook), goroutineCreateHook: new(goroutineCreateHook), goroutineExitHook: new(goroutineExitHook), goroutines: make(map[proc.Word]*Goroutine), } // Fill in remote runtime p.bootstrap() switch { case p.sys.allg.addr().base == 0: return nil, FormatError("failed to find runtime symbol 'allg'") case p.sys.g0.addr().base == 0: return nil, FormatError("failed to find runtime symbol 'g0'") case p.sys.newprocreadylocked == nil: return nil, FormatError("failed to find runtime symbol 'newprocreadylocked'") case p.sys.goexit == nil: return nil, FormatError("failed to find runtime symbol 'sys.goexit'") } // Get current goroutines p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false} err := try(func(a aborter) { g := p.sys.allg.aGet(a) for g != nil { gs := g.(remoteStruct) fmt.Printf("*** Found goroutine at %#x\n", gs.addr().base) p.goroutines[gs.addr().base] = &Goroutine{gs, nil, false} g = gs.field(p.f.G.Alllink).(remotePtr).aGet(a) } }) if err != nil { return nil, err } // Create internal breakpoints to catch new and exited goroutines p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry)).(*breakpointHook).addHandler(readylockedBP, true) p.OnBreakpoint(proc.Word(p.sys.goexit.Entry)).(*breakpointHook).addHandler(goexitBP, true) // Select current frames for _, g := range p.goroutines { g.resetFrame() } p.selectSomeGoroutine() return p, nil }
// bootstrap constructs the runtime structure of a remote process. func (p *Process) bootstrap() { // Manually construct runtime types p.runtime.String = newManualType(eval.TypeOfNative(rt1String{}), p.Arch) p.runtime.Slice = newManualType(eval.TypeOfNative(rt1Slice{}), p.Arch) p.runtime.Eface = newManualType(eval.TypeOfNative(rt1Eface{}), p.Arch) p.runtime.Type = newManualType(eval.TypeOfNative(rt1Type{}), p.Arch) p.runtime.CommonType = newManualType(eval.TypeOfNative(rt1CommonType{}), p.Arch) p.runtime.UncommonType = newManualType(eval.TypeOfNative(rt1UncommonType{}), p.Arch) p.runtime.StructField = newManualType(eval.TypeOfNative(rt1StructField{}), p.Arch) p.runtime.StructType = newManualType(eval.TypeOfNative(rt1StructType{}), p.Arch) p.runtime.PtrType = newManualType(eval.TypeOfNative(rt1PtrType{}), p.Arch) p.runtime.ArrayType = newManualType(eval.TypeOfNative(rt1ArrayType{}), p.Arch) p.runtime.SliceType = newManualType(eval.TypeOfNative(rt1SliceType{}), p.Arch) p.runtime.Stktop = newManualType(eval.TypeOfNative(rt1Stktop{}), p.Arch) p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch) p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch) // Get addresses of type.*runtime.XType for discrimination. rtv := reflect.Indirect(reflect.NewValue(&p.runtime)).(*reflect.StructValue) rtvt := rtv.Type().(*reflect.StructType) for i := 0; i < rtv.NumField(); i++ { n := rtvt.Field(i).Name if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' { continue } sym := p.syms.LookupSym("type.*runtime." + n[1:]) if sym == nil { continue } rtv.Field(i).(*reflect.UintValue).Set(sym.Value) } // Get runtime field indexes fillRuntimeIndexes(&p.runtime, &p.f) // Fill G status p.runtime.runtimeGStatus = rt1GStatus // Get globals p.sys.lessstack = p.syms.LookupFunc("sys.lessstack") p.sys.goexit = p.syms.LookupFunc("goexit") p.sys.newproc = p.syms.LookupFunc("sys.newproc") p.sys.deferproc = p.syms.LookupFunc("sys.deferproc") p.sys.newprocreadylocked = p.syms.LookupFunc("newprocreadylocked") if allg := p.syms.LookupSym("allg"); allg != nil { p.sys.allg = remotePtr{remote{proc.Word(allg.Value), p}, p.runtime.G} } if g0 := p.syms.LookupSym("g0"); g0 != nil { p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct) } }
func aNewFrame(a aborter, g remoteStruct) *Frame { p := g.r.p var pc, sp proc.Word // Is this G alive? switch g.field(p.f.G.Status).(remoteInt).aGet(a) { case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead: return nil } // Find the OS thread for this G // TODO(austin) Ideally, we could look at the G's state and // figure out if it's on an OS thread or not. However, this // is difficult because the state isn't updated atomically // with scheduling changes. for _, t := range p.proc.Threads() { regs, err := t.Regs() if err != nil { // TODO(austin) What to do? continue } thisg := p.G(regs) if thisg == g.addr().base { // Found this G's OS thread pc = regs.PC() sp = regs.SP() // If this thread crashed, try to recover it if pc == 0 { pc = p.peekUintptr(a, pc) sp += 8 } break } } if pc == 0 && sp == 0 { // G is not mapped to an OS thread. Use the // scheduler's stored PC and SP. sched := g.field(p.f.G.Sched).(remoteStruct) pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) } // Get Stktop stk := g.field(p.f.G.Stackbase).(remotePtr).aGet(a).(remoteStruct) return prepareFrame(a, pc, sp, stk, nil) }
func (v remotePtr) aGet(a aborter) eval.Value { addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())) if addr == 0 { return nil } return v.elemType.mk(remote{addr, v.r.p}) }
func (ArchLSB) ToWord(data []byte) proc.Word { var v proc.Word for i, b := range data { v |= proc.Word(b) << (uint(i) * 8) } return v }
func (v remote) Set(a aborter, size int, x uint64) { var arr [8]byte buf := arr[0:size] v.p.FromWord(proc.Word(x), buf) _, err := v.p.Poke(v.base, buf) if err != nil { a.Abort(err) } }
func (v remoteSlice) aGet(a aborter) eval.Slice { rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)) nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a) cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a) if base == 0 { return eval.Slice{nil, nel, cap} } return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap} }
// 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 }
func (v remoteString) aGet(a aborter) string { rs := v.r.p.runtime.String.mk(v.r).(remoteStruct) str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)) len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a) bytes := make([]uint8, len) _, err := v.r.p.Peek(str, bytes) if err != nil { a.Abort(err) } return string(bytes) }
func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { // TODO(austin) This probably shouldn't take a symbol name. // Perhaps it should take an interface that provides PC's. // Functions and instructions can implement that interface and // we can have something to translate file:line pairs. if curProc == nil { t.Abort(NoCurrentGoroutine{}) } name := args[0].(eval.StringValue).Get(t) fn := curProc.syms.LookupFunc(name) if fn == nil { t.Abort(UsageError("no such function " + name)) } curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop) }
func readylockedBP(ev Event) (EventAction, os.Error) { b := ev.(*Breakpoint) p := b.Process() // The new g is the only argument to this function, so the // stack will have the return address, then the G*. regs, err := b.osThread.Regs() if err != nil { return EAStop, err } sp := regs.SP() addr := sp + proc.Word(p.PtrSize()) arg := remotePtr{remote{addr, p}, p.runtime.G} var gp eval.Value err = try(func(a aborter) { gp = arg.aGet(a) }) if err != nil { return EAStop, err } if gp == nil { return EAStop, UnknownGoroutine{b.osThread, 0} } gs := gp.(remoteStruct) g := &Goroutine{gs, nil, false} p.goroutines[gs.addr().base] = g // Enqueue goroutine creation event parent := b.Goroutine() if parent.isG0() { parent = nil } p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent}) // If we don't have any thread selected, select this one if p.curGoroutine == nil { p.curGoroutine = g } return EADefault, nil }
// causesToEvents translates the stop causes of the underlying process // into an event queue. func (p *Process) causesToEvents() ([]Event, os.Error) { // Count causes we're interested in nev := 0 for _, t := range p.proc.Threads() { if c, err := t.Stopped(); err == nil { switch c := c.(type) { case proc.Breakpoint: nev++ case proc.Signal: // TODO(austin) //nev++; } } } // Translate causes to events events := make([]Event, nev) i := 0 for _, t := range p.proc.Threads() { if c, err := t.Stopped(); err == nil { switch c := c.(type) { case proc.Breakpoint: gt, err := p.osThreadToGoroutine(t) if err != nil { return nil, err } events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)} i++ case proc.Signal: // TODO(austin) } } } return events, nil }
// prepareFrame creates a Frame from the PC and SP within that frame, // as well as the active stack segment. This function takes care of // traversing stack breaks and unwinding closures. func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame { // Based on src/pkg/runtime/amd64/traceback.c:traceback p := stk.r.p top := inner == nil // Get function var path string var line int var fn *gosym.Func for i := 0; i < 100; i++ { // Traverse segmented stack breaks if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) { // Get stk->gobuf.pc pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) // Get stk->gobuf.sp sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) // Get stk->stackbase stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct) continue } // Get the PC of the call instruction callpc := pc if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) { callpc-- } // Look up function path, line, fn = p.syms.PCToLine(uint64(callpc)) if fn != nil { break } // Closure? var buf = make([]byte, p.ClosureSize()) if _, err := p.Peek(pc, buf); err != nil { break } spdelta, ok := p.ParseClosure(buf) if ok { sp += proc.Word(spdelta) pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize())) } } if fn == nil { return nil } // Compute frame pointer var fp proc.Word if fn.FrameSize < p.PtrSize() { fp = sp + proc.Word(p.PtrSize()) } else { fp = sp + proc.Word(fn.FrameSize) } // TODO(austin) To really figure out if we're in the prologue, // we need to disassemble the function and look for the call // to morestack. For now, just special case the entry point. // // TODO(austin) What if we're in the call to morestack in the // prologue? Then top == false. if top && pc == proc.Word(fn.Entry) { // We're in the function prologue, before SP // has been adjusted for the frame. fp -= proc.Word(fn.FrameSize - p.PtrSize()) } return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil} }
func (v remoteStruct) field(i int) eval.Value { f := &v.layout[i] return f.fieldType.mk(v.r.plus(proc.Word(f.offset))) }
func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType} }
func (v remoteArray) elem(i int64) eval.Value { return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))) }
// parseRemoteType parses a Type structure in a remote process to // construct the corresponding interpreter type and remote type. func parseRemoteType(a aborter, rs remoteStruct) *remoteType { addr := rs.addr().base p := rs.addr().p // We deal with circular types by discovering cycles at // NamedTypes. If a type cycles back to something other than // a named type, we're guaranteed that there will be a named // type somewhere in that cycle. Thus, we continue down, // re-parsing types until we reach the named type in the // cycle. In order to still create one remoteType per remote // type, we insert an empty remoteType in the type map the // first time we encounter the type and re-use that structure // the second time we encounter it. rt, ok := p.types[addr] if ok && rt.Type != nil { return rt } else if !ok { rt = &remoteType{} p.types[addr] = rt } if debugParseRemoteType { sym := p.syms.SymByAddr(uint64(addr)) name := "<unknown>" if sym != nil { name = sym.Name } log.Stderrf("%sParsing type at %#x (%s)", prtIndent, addr, name) prtIndent += " " defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }() } // Get Type header itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)) typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct) // Is this a named type? var nt *eval.NamedType uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a) if uncommon != nil { name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a) if name != nil { // TODO(austin) Declare type in appropriate remote package nt = eval.NewNamedType(name.(remoteString).aGet(a)) rt.Type = nt } } // Create type var t eval.Type var mk maker switch itype { case p.runtime.PBoolType: t = eval.BoolType mk = mkBool case p.runtime.PUint8Type: t = eval.Uint8Type mk = mkUint8 case p.runtime.PUint16Type: t = eval.Uint16Type mk = mkUint16 case p.runtime.PUint32Type: t = eval.Uint32Type mk = mkUint32 case p.runtime.PUint64Type: t = eval.Uint64Type mk = mkUint64 case p.runtime.PUintType: t = eval.UintType mk = mkUint case p.runtime.PUintptrType: t = eval.UintptrType mk = mkUintptr case p.runtime.PInt8Type: t = eval.Int8Type mk = mkInt8 case p.runtime.PInt16Type: t = eval.Int16Type mk = mkInt16 case p.runtime.PInt32Type: t = eval.Int32Type mk = mkInt32 case p.runtime.PInt64Type: t = eval.Int64Type mk = mkInt64 case p.runtime.PIntType: t = eval.IntType mk = mkInt case p.runtime.PFloat32Type: t = eval.Float32Type mk = mkFloat32 case p.runtime.PFloat64Type: t = eval.Float64Type mk = mkFloat64 case p.runtime.PFloatType: t = eval.FloatType mk = mkFloat case p.runtime.PStringType: t = eval.StringType mk = mkString case p.runtime.PArrayType: // Cast to an ArrayType typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct) len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a)) elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewArrayType(len, elem.Type) mk = func(r remote) eval.Value { return remoteArray{r, len, elem} } case p.runtime.PStructType: // Cast to a StructType typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct) fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a) fields := make([]eval.StructField, fs.Len) layout := make([]remoteStructField, fs.Len) for i := range fields { f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct) elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct) elem := parseRemoteType(a, elemrs) fields[i].Type = elem.Type name := f.field(p.f.StructField.Name).(remotePtr).aGet(a) if name == nil { fields[i].Anonymous = true } else { fields[i].Name = name.(remoteString).aGet(a) } layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a)) layout[i].fieldType = elem } t = eval.NewStructType(fields) mk = func(r remote) eval.Value { return remoteStruct{r, layout} } case p.runtime.PPtrType: // Cast to a PtrType typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct) elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewPtrType(elem.Type) mk = func(r remote) eval.Value { return remotePtr{r, elem} } case p.runtime.PSliceType: // Cast to a SliceType typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct) elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewSliceType(elem.Type) mk = func(r remote) eval.Value { return remoteSlice{r, elem} } case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType: // TODO(austin) t = eval.UintptrType mk = mkUintptr default: sym := p.syms.SymByAddr(uint64(itype)) name := "<unknown symbol>" if sym != nil { name = sym.Name } err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name) a.Abort(FormatError(err)) } // Fill in the remote type if nt != nil { nt.Complete(t) } else { rt.Type = t } rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a)) rt.mk = mk return rt }
func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word { return proc.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)) }
// 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.Stderrf("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.Stderrf("while defining package %s: %v", pkgName, err) } } return nil }