| // Copyright 2017 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 gocore |
| |
| import ( |
| "debug/dwarf" |
| "fmt" |
| "reflect" |
| "regexp" |
| "sort" |
| "strings" |
| |
| "golang.org/x/debug/internal/core" |
| ) |
| |
| const ( |
| AttrGoKind dwarf.Attr = 0x2900 |
| ) |
| |
| // read DWARF types from core dump. |
| func (p *Process) readDWARFTypes() { |
| d, _ := p.proc.DWARF() |
| |
| // Make one of our own Types for each dwarf type. |
| r := d.Reader() |
| var types []*Type |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| switch e.Tag { |
| case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: |
| dt, err := d.Type(e.Offset) |
| if err != nil { |
| continue |
| } |
| t := &Type{Name: gocoreName(dt), Size: dwarfSize(dt, p.proc.PtrSize())} |
| if goKind, ok := e.Val(AttrGoKind).(int64); ok { |
| t.goKind = reflect.Kind(goKind) |
| } |
| p.dwarfMap[dt] = t |
| types = append(types, t) |
| } |
| } |
| |
| p.runtimeNameMap = map[string][]*Type{} |
| |
| // Fill in fields of types. Postponed until now so we're sure |
| // we have all the Types allocated and available. |
| for dt, t := range p.dwarfMap { |
| switch x := dt.(type) { |
| case *dwarf.ArrayType: |
| t.Kind = KindArray |
| t.Elem = p.dwarfMap[x.Type] |
| t.Count = x.Count |
| case *dwarf.PtrType: |
| t.Kind = KindPtr |
| // unsafe.Pointer has a void base type. |
| if _, ok := x.Type.(*dwarf.VoidType); !ok { |
| t.Elem = p.dwarfMap[x.Type] |
| } |
| case *dwarf.StructType: |
| t.Kind = KindStruct |
| for _, f := range x.Field { |
| fType := p.dwarfMap[f.Type] |
| if fType == nil { |
| // Weird case: arrays of size 0 in structs, like |
| // Sysinfo_t.X_f. Synthesize a type so things later don't |
| // get sad. |
| if arr, ok := f.Type.(*dwarf.ArrayType); ok && arr.Count == 0 { |
| fType = &Type{ |
| Name: f.Type.String(), |
| Kind: KindArray, |
| Count: arr.Count, |
| Elem: p.dwarfMap[arr.Type], |
| } |
| } else { |
| panic(fmt.Sprintf( |
| "found a nil ftype for field %s.%s, type %s (%s) on ", |
| x.StructName, f.Name, f.Type, reflect.TypeOf(f.Type))) |
| } |
| } |
| |
| // Work around issue 21094. There's no guarantee that the |
| // pointer type is in the DWARF, so just invent a Type. |
| if strings.HasPrefix(t.Name, "sudog<") && f.Name == "elem" && |
| strings.Count(t.Name, "*")+1 != strings.Count(gocoreName(f.Type), "*") { |
| ptrName := "*" + gocoreName(f.Type) |
| fType = &Type{Name: ptrName, Kind: KindPtr, Size: p.proc.PtrSize(), Elem: fType} |
| p.runtimeNameMap[ptrName] = []*Type{fType} |
| } |
| |
| t.Fields = append(t.Fields, Field{Name: f.Name, Type: fType, Off: f.ByteOffset}) |
| } |
| case *dwarf.BoolType: |
| t.Kind = KindBool |
| case *dwarf.IntType: |
| t.Kind = KindInt |
| case *dwarf.UintType: |
| t.Kind = KindUint |
| case *dwarf.FloatType: |
| t.Kind = KindFloat |
| case *dwarf.ComplexType: |
| t.Kind = KindComplex |
| case *dwarf.FuncType: |
| t.Kind = KindFunc |
| case *dwarf.TypedefType: |
| // handle these types in the loop below |
| default: |
| panic(fmt.Sprintf("unknown type %s %T", dt, dt)) |
| } |
| } |
| |
| // Detect strings & slices |
| for _, t := range types { |
| if t.Kind != KindStruct { |
| continue |
| } |
| switch t.goKind { |
| case reflect.String: |
| t.Kind = KindString |
| t.Elem = t.Fields[0].Type.Elem // TODO: check that it is always uint8. |
| t.Fields = nil |
| case reflect.Slice: |
| t.Kind = KindSlice |
| t.Elem = t.Fields[0].Type.Elem |
| t.Fields = nil |
| } |
| } |
| |
| // Copy info from base types into typedefs. |
| for dt, t := range p.dwarfMap { |
| tt, ok := dt.(*dwarf.TypedefType) |
| if !ok { |
| continue |
| } |
| base := tt.Type |
| // Walk typedef chain until we reach a non-typedef type. |
| for { |
| if x, ok := base.(*dwarf.TypedefType); ok { |
| base = x.Type |
| continue |
| } |
| break |
| } |
| bt := p.dwarfMap[base] |
| |
| // Copy type info from base. Everything except the name. |
| name := t.Name |
| *t = *bt |
| t.Name = name |
| |
| // Detect some special types. If the base is some particular type, |
| // then the alias gets marked as special. |
| // We have aliases like: |
| // interface {} -> struct runtime.eface |
| // error -> struct runtime.iface |
| // Note: the base itself does not get marked as special. |
| // (Unlike strings and slices, where they do.) |
| if bt.Name == "runtime.eface" { |
| t.Kind = KindEface |
| t.Fields = nil |
| } |
| if bt.Name == "runtime.iface" { |
| t.Kind = KindIface |
| t.Fields = nil |
| } |
| } |
| |
| // Make a runtime name -> Type map for existing DWARF types. |
| for dt, t := range p.dwarfMap { |
| name := runtimeName(dt) |
| p.runtimeNameMap[name] = append(p.runtimeNameMap[name], t) |
| } |
| |
| // Construct the runtime.specialfinalizer type. It won't be found |
| // in DWARF before 1.10 because it does not appear in the type of any variable. |
| // type specialfinalizer struct { |
| // special special |
| // fn *funcval |
| // nret uintptr |
| // fint *_type |
| // ot *ptrtype |
| // } |
| if p.runtimeNameMap["runtime.specialfinalizer"] == nil { |
| special := p.findType("runtime.special") |
| p.runtimeNameMap["runtime.specialfinalizer"] = []*Type{ |
| &Type{ |
| Name: "runtime.specialfinalizer", |
| Size: special.Size + 4*p.proc.PtrSize(), |
| Kind: KindStruct, |
| Fields: []Field{ |
| Field{ |
| Name: "special", |
| Off: 0, |
| Type: special, |
| }, |
| Field{ |
| Name: "fn", |
| Off: special.Size, |
| Type: p.findType("*runtime.funcval"), |
| }, |
| Field{ |
| Name: "nret", |
| Off: special.Size + p.proc.PtrSize(), |
| Type: p.findType("uintptr"), |
| }, |
| Field{ |
| Name: "fint", |
| Off: special.Size + 2*p.proc.PtrSize(), |
| Type: p.findType("*runtime._type"), |
| }, |
| Field{ |
| Name: "fn", |
| Off: special.Size + 3*p.proc.PtrSize(), |
| Type: p.findType("*runtime.ptrtype"), |
| }, |
| }, |
| }, |
| } |
| } |
| } |
| |
| func isNonGoCU(e *dwarf.Entry) bool { |
| if e.Tag != dwarf.TagCompileUnit { |
| return false |
| } |
| prod, ok := e.Val(dwarf.AttrProducer).(string) |
| if !ok { |
| return true |
| } |
| return !strings.Contains(prod, "Go cmd/compile") |
| } |
| |
| // dwarfSize is used to compute the size of a DWARF type. |
| // dt.Size() is wrong when it returns a negative number. |
| // This function implements just enough to correct the bad behavior. |
| func dwarfSize(dt dwarf.Type, ptrSize int64) int64 { |
| s := dt.Size() |
| if s >= 0 { |
| return s |
| } |
| switch x := dt.(type) { |
| case *dwarf.FuncType: |
| return ptrSize // Fix for issue 21097. |
| case *dwarf.ArrayType: |
| return x.Count * dwarfSize(x.Type, ptrSize) |
| case *dwarf.TypedefType: |
| return dwarfSize(x.Type, ptrSize) |
| default: |
| panic(fmt.Sprintf("unhandled: %s, %T", x, x)) |
| } |
| } |
| |
| // gocoreName generates the name this package uses to refer to a dwarf type. |
| // This name differs from the dwarf name in that it stays closer to the Go name for the type. |
| // For instance (dwarf name -> gocoreName) |
| // |
| // struct runtime.siginfo -> runtime.siginfo |
| // *void -> unsafe.Pointer |
| // struct struct { runtime.signalLock uint32; runtime.hz int32 } -> struct { signalLock uint32; hz int32 } |
| func gocoreName(dt dwarf.Type) string { |
| switch x := dt.(type) { |
| case *dwarf.PtrType: |
| if _, ok := x.Type.(*dwarf.VoidType); ok { |
| return "unsafe.Pointer" |
| } |
| return "*" + gocoreName(x.Type) |
| case *dwarf.ArrayType: |
| return fmt.Sprintf("[%d]%s", x.Count, gocoreName(x.Type)) |
| case *dwarf.StructType: |
| if !strings.HasPrefix(x.StructName, "struct {") { |
| // This is a named type, return that name. |
| return x.StructName |
| } |
| // Build gocore name from the DWARF fields. |
| s := "struct {" |
| first := true |
| for _, f := range x.Field { |
| if !first { |
| s += ";" |
| } |
| name := f.Name |
| if i := strings.Index(name, "."); i >= 0 { |
| // Remove pkg path from field names. |
| name = name[i+1:] |
| } |
| s += fmt.Sprintf(" %s %s", name, gocoreName(f.Type)) |
| first = false |
| } |
| s += " }" |
| return s |
| default: |
| return dt.String() |
| } |
| } |
| |
| // Generate the name the runtime uses for a dwarf type. The DWARF generator |
| // and the runtime use slightly different names for the same underlying type. |
| func runtimeName(dt dwarf.Type) string { |
| switch x := dt.(type) { |
| case *dwarf.PtrType: |
| if _, ok := x.Type.(*dwarf.VoidType); ok { |
| return "unsafe.Pointer" |
| } |
| return "*" + runtimeName(x.Type) |
| case *dwarf.ArrayType: |
| return fmt.Sprintf("[%d]%s", x.Count, runtimeName(x.Type)) |
| case *dwarf.StructType: |
| if !strings.HasPrefix(x.StructName, "struct {") { |
| // This is a named type, return that name. |
| return stripPackagePath(x.StructName) |
| } |
| // Figure out which fields have anonymous names. |
| var anon []bool |
| for _, f := range strings.Split(x.StructName[8:len(x.StructName)-1], ";") { |
| f = strings.TrimSpace(f) |
| anon = append(anon, !strings.Contains(f, " ")) |
| // TODO: this isn't perfect. If the field type has a space in it, |
| // then this logic doesn't work. Need to search for keyword for |
| // field type, like "interface", "struct", ... |
| } |
| // Make sure anon is long enough. This probably never triggers. |
| for len(anon) < len(x.Field) { |
| anon = append(anon, false) |
| } |
| |
| // Build runtime name from the DWARF fields. |
| s := "struct {" |
| first := true |
| for _, f := range x.Field { |
| if !first { |
| s += ";" |
| } |
| name := f.Name |
| if i := strings.Index(name, "."); i >= 0 { |
| name = name[i+1:] |
| } |
| if anon[0] { |
| s += fmt.Sprintf(" %s", runtimeName(f.Type)) |
| } else { |
| s += fmt.Sprintf(" %s %s", name, runtimeName(f.Type)) |
| } |
| first = false |
| anon = anon[1:] |
| } |
| s += " }" |
| return s |
| default: |
| return stripPackagePath(dt.String()) |
| } |
| } |
| |
| var pathRegexp = regexp.MustCompile(`([\w.-]+/)+\w+`) |
| |
| func stripPackagePath(name string) string { |
| // The runtime uses just package names. DWARF uses whole package paths. |
| // To convert from the latter to the former, get rid of the package paths. |
| // Examples: |
| // text/template.Template -> template.Template |
| // map[string]compress/gzip.Writer -> map[string]gzip.Writer |
| return pathRegexp.ReplaceAllStringFunc(name, func(path string) string { |
| return path[strings.LastIndex(path, "/")+1:] |
| }) |
| } |
| |
| // readRuntimeConstants populates the p.rtConstants map. |
| func (p *Process) readRuntimeConstants() { |
| p.rtConstants = map[string]int64{} |
| |
| // Hardcoded values for Go 1.9. |
| // (Go did not have constants in DWARF before 1.10.) |
| m := p.rtConstants |
| m["_MSpanDead"] = 0 |
| m["_MSpanInUse"] = 1 |
| m["_MSpanManual"] = 2 |
| m["_MSpanFree"] = 3 |
| m["_Gidle"] = 0 |
| m["_Grunnable"] = 1 |
| m["_Grunning"] = 2 |
| m["_Gsyscall"] = 3 |
| m["_Gwaiting"] = 4 |
| m["_Gdead"] = 6 |
| m["_Gscan"] = 0x1000 |
| m["_PCDATA_StackMapIndex"] = 0 |
| m["_FUNCDATA_LocalsPointerMaps"] = 1 |
| m["_FUNCDATA_ArgsPointerMaps"] = 0 |
| m["tflagExtraStar"] = 1 << 1 |
| m["kindGCProg"] = 1 << 6 |
| m["kindDirectIface"] = 1 << 5 |
| m["_PageSize"] = 1 << 13 |
| m["_KindSpecialFinalizer"] = 1 |
| |
| // From 1.10, these constants are recorded in DWARF records. |
| d, _ := p.proc.DWARF() |
| r := d.Reader() |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if e.Tag != dwarf.TagConstant { |
| continue |
| } |
| f := e.AttrField(dwarf.AttrName) |
| if f == nil { |
| continue |
| } |
| name := f.Val.(string) |
| if !strings.HasPrefix(name, "runtime.") { |
| continue |
| } |
| name = name[8:] |
| c := e.AttrField(dwarf.AttrConstValue) |
| if c == nil { |
| continue |
| } |
| p.rtConstants[name] = c.Val.(int64) |
| } |
| } |
| |
| const ( |
| _DW_OP_addr = 0x03 |
| _DW_OP_call_frame_cfa = 0x9c |
| _DW_OP_plus = 0x22 |
| _DW_OP_consts = 0x11 |
| ) |
| |
| func (p *Process) readGlobals() { |
| d, _ := p.proc.DWARF() |
| r := d.Reader() |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| |
| if e.Tag != dwarf.TagVariable { |
| continue |
| } |
| f := e.AttrField(dwarf.AttrLocation) |
| if f == nil { |
| continue |
| } |
| if f.Class != dwarf.ClassExprLoc { |
| // Globals are all encoded with this class. |
| continue |
| } |
| loc := f.Val.([]byte) |
| if len(loc) == 0 || loc[0] != _DW_OP_addr { |
| continue |
| } |
| var a core.Address |
| if p.proc.PtrSize() == 8 { |
| a = core.Address(p.proc.ByteOrder().Uint64(loc[1:])) |
| } else { |
| a = core.Address(p.proc.ByteOrder().Uint32(loc[1:])) |
| } |
| a = a.Add(int64(p.proc.StaticBase())) |
| if !p.proc.Writeable(a) { |
| // Read-only globals can't have heap pointers. |
| // TODO: keep roots around anyway? |
| continue |
| } |
| f = e.AttrField(dwarf.AttrType) |
| if f == nil { |
| continue |
| } |
| dt, err := d.Type(f.Val.(dwarf.Offset)) |
| if err != nil { |
| panic(err) |
| } |
| if _, ok := dt.(*dwarf.UnspecifiedType); ok { |
| continue // Ignore markers like data/edata. |
| } |
| nf := e.AttrField(dwarf.AttrName) |
| if nf == nil { |
| continue |
| } |
| p.globals = append(p.globals, &Root{ |
| Name: nf.Val.(string), |
| Addr: a, |
| Type: p.dwarfMap[dt], |
| Frame: nil, |
| }) |
| } |
| } |
| |
| func (p *Process) readStackVars() { |
| type Var struct { |
| name string |
| off int64 |
| typ *Type |
| } |
| vars := map[*Func][]Var{} |
| var curfn *Func |
| d, _ := p.proc.DWARF() |
| r := d.Reader() |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| |
| if e.Tag == dwarf.TagSubprogram { |
| lowpc := e.AttrField(dwarf.AttrLowpc) |
| highpc := e.AttrField(dwarf.AttrHighpc) |
| if lowpc == nil || highpc == nil { |
| continue |
| } |
| min := core.Address(lowpc.Val.(uint64)) |
| max := core.Address(highpc.Val.(uint64)) |
| f := p.funcTab.find(min) |
| if f == nil { |
| // some func Go doesn't know about. C? |
| curfn = nil |
| } else { |
| if f.entry != min { |
| panic("dwarf and runtime don't agree about start of " + f.name) |
| } |
| if p.funcTab.find(max-1) != f { |
| panic("function ranges don't match for " + f.name) |
| } |
| curfn = f |
| } |
| continue |
| } |
| if e.Tag != dwarf.TagVariable && e.Tag != dwarf.TagFormalParameter { |
| continue |
| } |
| aloc := e.AttrField(dwarf.AttrLocation) |
| if aloc == nil { |
| continue |
| } |
| if aloc.Class != dwarf.ClassExprLoc { |
| // TODO: handle ClassLocListPtr here. |
| // As of go 1.11, locals are encoded this way. |
| // Until we fix this TODO, viewcore will not be able to |
| // show local variables. |
| continue |
| } |
| // Interpret locations of the form |
| // DW_OP_call_frame_cfa |
| // DW_OP_consts <off> |
| // DW_OP_plus |
| // (with possibly missing DW_OP_consts & DW_OP_plus for the zero offset.) |
| // TODO: handle other possible locations (e.g. register locations). |
| loc := aloc.Val.([]byte) |
| if len(loc) == 0 || loc[0] != _DW_OP_call_frame_cfa { |
| continue |
| } |
| loc = loc[1:] |
| var off int64 |
| if len(loc) != 0 && loc[0] == _DW_OP_consts { |
| loc = loc[1:] |
| var s uint |
| for len(loc) > 0 { |
| b := loc[0] |
| loc = loc[1:] |
| off += int64(b&0x7f) << s |
| s += 7 |
| if b&0x80 == 0 { |
| break |
| } |
| } |
| off = off << (64 - s) >> (64 - s) |
| if len(loc) == 0 || loc[0] != _DW_OP_plus { |
| continue |
| } |
| loc = loc[1:] |
| } |
| if len(loc) != 0 { |
| continue // more stuff we don't recognize |
| } |
| f := e.AttrField(dwarf.AttrType) |
| if f == nil { |
| continue |
| } |
| dt, err := d.Type(f.Val.(dwarf.Offset)) |
| if err != nil { |
| panic(err) |
| } |
| nf := e.AttrField(dwarf.AttrName) |
| if nf == nil { |
| continue |
| } |
| name := nf.Val.(string) |
| vars[curfn] = append(vars[curfn], Var{name: name, off: off, typ: p.dwarfMap[dt]}) |
| } |
| |
| // Get roots from goroutine stacks. |
| for _, g := range p.goroutines { |
| for _, f := range g.frames { |
| // Start with all pointer slots as unnamed. |
| unnamed := map[core.Address]bool{} |
| for a := range f.Live { |
| unnamed[a] = true |
| } |
| // Emit roots for DWARF entries. |
| for _, v := range vars[f.f] { |
| r := &Root{ |
| Name: v.name, |
| Addr: f.max.Add(v.off), |
| Type: v.typ, |
| Frame: f, |
| } |
| f.roots = append(f.roots, r) |
| // Remove this variable from the set of unnamed pointers. |
| for a := r.Addr; a < r.Addr.Add(r.Type.Size); a = a.Add(p.proc.PtrSize()) { |
| delete(unnamed, a) |
| } |
| } |
| // Emit roots for unnamed pointer slots in the frame. |
| // Make deterministic by sorting first. |
| s := make([]core.Address, 0, len(unnamed)) |
| for a := range unnamed { |
| s = append(s, a) |
| } |
| sort.Slice(s, func(i, j int) bool { return s[i] < s[j] }) |
| for _, a := range s { |
| r := &Root{ |
| Name: "unk", |
| Addr: a, |
| Type: p.findType("unsafe.Pointer"), |
| Frame: f, |
| } |
| f.roots = append(f.roots, r) |
| } |
| } |
| } |
| } |
| |
| /* Dwarf encoding notes |
| |
| type XXX sss |
| |
| translates to a dwarf type pkg.XXX of the type of sss (uint, float, ...) |
| |
| exception: if sss is a struct or array, then we get two types, the "unnamed" and "named" type. |
| The unnamed type is a dwarf struct type with name "struct pkg.XXX" or a dwarf array type with |
| name [N]elem. |
| Then there is a typedef with pkg.XXX pointing to "struct pkg.XXX" or [N]elem. |
| |
| For structures, lowercase field names are prepended with the package name (pkg path?). |
| |
| type XXX interface{} |
| pkg.XXX is a typedef to "struct runtime.eface" |
| type XXX interface{f()} |
| pkg.XXX is a typedef to "struct runtime.iface" |
| |
| Sometimes there is even a chain of identically-named typedefs. I have no idea why. |
| main.XXX -> main.XXX -> struct runtime.iface |
| |
| */ |