| // 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/proc" |
| "exp/eval" |
| "fmt" |
| "log" |
| ) |
| |
| const debugParseRemoteType = false |
| |
| // A remoteType is the local representation of a type in a remote process. |
| type remoteType struct { |
| eval.Type |
| // The size of values of this type in bytes. |
| size int |
| // The field alignment of this type. Only used for |
| // manually-constructed types. |
| fieldAlign int |
| // The maker function to turn a remote address of a value of |
| // this type into an interpreter Value. |
| mk maker |
| } |
| |
| var manualTypes = make(map[Arch]map[eval.Type]*remoteType) |
| |
| // newManualType constructs a remote type from an interpreter Type |
| // using the size and alignment properties of the given architecture. |
| // Most types are parsed directly out of the remote process, but to do |
| // so we need to layout the structures that describe those types ourselves. |
| func newManualType(t eval.Type, arch Arch) *remoteType { |
| if nt, ok := t.(*eval.NamedType); ok { |
| t = nt.Def |
| } |
| |
| // Get the type map for this architecture |
| typeMap := manualTypes[arch] |
| if typeMap == nil { |
| typeMap = make(map[eval.Type]*remoteType) |
| manualTypes[arch] = typeMap |
| |
| // Construct basic types for this architecture |
| basicType := func(t eval.Type, mk maker, size int, fieldAlign int) { |
| t = t.(*eval.NamedType).Def |
| if fieldAlign == 0 { |
| fieldAlign = size |
| } |
| typeMap[t] = &remoteType{t, size, fieldAlign, mk} |
| } |
| basicType(eval.Uint8Type, mkUint8, 1, 0) |
| basicType(eval.Uint32Type, mkUint32, 4, 0) |
| basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0) |
| basicType(eval.Int16Type, mkInt16, 2, 0) |
| basicType(eval.Int32Type, mkInt32, 4, 0) |
| basicType(eval.IntType, mkInt, arch.IntSize(), 0) |
| basicType(eval.StringType, mkString, arch.PtrSize()+arch.IntSize(), arch.PtrSize()) |
| } |
| |
| if rt, ok := typeMap[t]; ok { |
| return rt |
| } |
| |
| var rt *remoteType |
| switch t := t.(type) { |
| case *eval.PtrType: |
| var elem *remoteType |
| mk := func(r remote) eval.Value { return remotePtr{r, elem} } |
| rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk} |
| // Construct the element type after registering the |
| // type to break cycles. |
| typeMap[eval.Type(t)] = rt |
| elem = newManualType(t.Elem, arch) |
| |
| case *eval.ArrayType: |
| elem := newManualType(t.Elem, arch) |
| mk := func(r remote) eval.Value { return remoteArray{r, t.Len, elem} } |
| rt = &remoteType{t, elem.size * int(t.Len), elem.fieldAlign, mk} |
| |
| case *eval.SliceType: |
| elem := newManualType(t.Elem, arch) |
| mk := func(r remote) eval.Value { return remoteSlice{r, elem} } |
| rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk} |
| |
| case *eval.StructType: |
| layout := make([]remoteStructField, len(t.Elems)) |
| offset := 0 |
| fieldAlign := 0 |
| for i, f := range t.Elems { |
| elem := newManualType(f.Type, arch) |
| if fieldAlign == 0 { |
| fieldAlign = elem.fieldAlign |
| } |
| offset = arch.Align(offset, elem.fieldAlign) |
| layout[i].offset = offset |
| layout[i].fieldType = elem |
| offset += elem.size |
| } |
| mk := func(r remote) eval.Value { return remoteStruct{r, layout} } |
| rt = &remoteType{t, offset, fieldAlign, mk} |
| |
| default: |
| log.Panicf("cannot manually construct type %T", t) |
| } |
| |
| typeMap[t] = rt |
| return rt |
| } |
| |
| var prtIndent = "" |
| |
| // 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.Printf("%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.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 |
| } |