Skip to content

Commit

Permalink
Convert runtime regions to use full-fledged Type structures
Browse files Browse the repository at this point in the history
instead of hacky types-as-strings.

Reorg the passes a bit so there aren't so many runtime-specific
data structures.
  • Loading branch information
randall77 committed Sep 19, 2017
1 parent 9b149ed commit eabef37
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 208 deletions.
2 changes: 1 addition & 1 deletion core/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (p *Process) readCore(core *os.File) error {
return err
}
if e.Type != elf.ET_CORE {
return fmt.Errorf("%s is not a core file", core)
return fmt.Errorf("%s is not a core file", core.Name())
}
switch e.Class {
case elf.ELFCLASS32:
Expand Down
73 changes: 31 additions & 42 deletions gocore/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func (p *Program) readDWARFTypes() {

// 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() {
switch e.Tag {
case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
Expand All @@ -27,8 +28,8 @@ func (p *Program) readDWARFTypes() {
size = dwarfSize(dt, p.proc.PtrSize())
}
t := &Type{name: gocoreName(dt), Size: size}
p.types = append(p.types, t)
p.dwarfMap[dt] = t
types = append(types, t)
}
}

Expand Down Expand Up @@ -71,7 +72,7 @@ func (p *Program) readDWARFTypes() {
}

// Detect strings & slices
for _, t := range p.types {
for _, t := range types {
if t.Kind != KindStruct {
continue
}
Expand Down Expand Up @@ -132,6 +133,13 @@ func (p *Program) readDWARFTypes() {
t.Fields = nil
}
}

// Make a runtime name -> Type map for existing DWARF types.
p.runtimeNameMap = map[string][]*Type{}
for dt, t := range p.dwarfMap {
name := runtimeName(dt)
p.runtimeNameMap[name] = append(p.runtimeNameMap[name], t)
}
}

// dwarfSize is used to compute the size of a DWARF type when .Size()
Expand Down Expand Up @@ -456,12 +464,12 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
}
ptr := r.ReadPtr(a.Add(ptrSize))
if t.Kind == KindIface {
typ = p.proc.ReadPtr(typ.Add(p.rtStructs["runtime.itab"].fields["_type"].off))
typ = p.proc.ReadPtr(typ.Add(p.findType("runtime.itab").field("_type").Off))
}
// TODO: for KindEface, type the typ pointer. It might point to the heap
// if the type was allocated with reflect.

direct := p.proc.ReadUint8(typ.Add(p.rtStructs["runtime._type"].fields["kind"].off))&uint8(p.rtConstants["kindDirectIface"]) != 0
direct := p.proc.ReadUint8(typ.Add(p.findType("runtime._type").field("kind").Off))&uint8(p.rtConstants["kindDirectIface"]) != 0
dt := p.runtimeType2Type(typ)
if direct {
// Find the base type of the pointer held in the interface.
Expand Down Expand Up @@ -514,7 +522,6 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
ft := f.closure
if ft == nil {
ft = &Type{name: "closure for " + f.name, Size: ptrSize, Kind: KindPtr}
p.types = append(p.types, ft)
// For now, treat a closure like an unsafe.Pointer.
// TODO: better value for size?
f.closure = ft
Expand Down Expand Up @@ -553,27 +560,8 @@ func (p *Program) typeObject(a core.Address, t *Type, r reader, add func(core.Ad
}
}

// findRuntimeInfo uses DWARF information to find all the struct sizes,
// field offsets, and field types for runtime data structures.
// It also finds a useful set of runtime constants.
// It populates p.rtStructs and rtConstants.
func (p *Program) findRuntimeInfo() {
p.rtStructs = map[string]structInfo{}
for dt, _ := range p.dwarfMap {
rtname := runtimeName(dt)
if !strings.HasPrefix(rtname, "runtime.") {
continue
}
x, ok := dt.(*dwarf.StructType)
if !ok {
continue
}
s := structInfo{size: dt.Size(), fields: map[string]fieldInfo{}}
for _, f := range x.Field {
s.fields[f.Name] = fieldInfo{off: f.ByteOffset, typ: runtimeName(f.Type)}
}
p.rtStructs[rtname] = s
}
// readRuntimeConstants populates the p.rtConstants map.
func (p *Program) readRuntimeConstants() {
p.rtConstants = map[string]int64{}
// TODO: It would be ideal if we could glean this info from DWARF.
// But we don't package up constants in DWARF right now. See issue #14517.
Expand All @@ -600,23 +588,22 @@ func (p *Program) findRuntimeInfo() {
m["_PageSize"] = 1 << 13
}

func (p *Program) findRoots() {
const (
DW_OP_addr = 0x03
DW_OP_call_frame_cfa = 0x9c
DW_OP_plus = 0x22
DW_OP_consts = 0x11
)
d, _ := p.proc.DWARF()
const (
_DW_OP_addr = 0x03
_DW_OP_call_frame_cfa = 0x9c
_DW_OP_plus = 0x22
_DW_OP_consts = 0x11
)

// Find global variables.
func (p *Program) readGlobals() {
d, _ := p.proc.DWARF()
r := d.Reader()
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
if e.Tag != dwarf.TagVariable {
continue
}
loc := e.AttrField(dwarf.AttrLocation).Val.([]byte)
if loc[0] != DW_OP_addr {
if loc[0] != _DW_OP_addr {
continue
}
var a core.Address
Expand Down Expand Up @@ -644,16 +631,18 @@ func (p *Program) findRoots() {
Live: nil,
})
}
}

// Find stack variables.
func (p *Program) readStackVars() {
type Var struct {
name string
off int64
typ *Type
}
vars := map[*Func][]Var{}
var curfn *Func
r = d.Reader()
d, _ := p.proc.DWARF()
r := d.Reader()
for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() {
if e.Tag == dwarf.TagSubprogram {
min := core.Address(e.AttrField(dwarf.AttrLowpc).Val.(uint64))
Expand Down Expand Up @@ -681,14 +670,14 @@ func (p *Program) findRoots() {
continue
}
loc := aloc.Val.([]byte)
if len(loc) == 0 || loc[0] != DW_OP_call_frame_cfa {
if len(loc) == 0 || loc[0] != _DW_OP_call_frame_cfa {
continue
}
loc = loc[1:]
var off int64
if len(loc) != 0 && loc[len(loc)-1] == DW_OP_plus {
if len(loc) != 0 && loc[len(loc)-1] == _DW_OP_plus {
loc = loc[:len(loc)-1]
if len(loc) == 0 || loc[0] != DW_OP_consts {
if len(loc) == 0 || loc[0] != _DW_OP_consts {
continue
}
loc = loc[1:]
Expand Down Expand Up @@ -748,7 +737,7 @@ func (p *Program) findRoots() {
r := &Root{
Name: "unk",
Addr: a,
Type: p.runtimeNameMap["unsafe.Pointer"][0],
Type: p.findType("unsafe.Pointer"),
Live: f.live,
}
f.roots = append(f.roots, r)
Expand Down
96 changes: 29 additions & 67 deletions gocore/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,13 @@ package gocore
import (
"debug/dwarf"
"fmt"
"strings"

"github.com/randall77/corelib/core"
)

// Core takes a loaded core file and extracts Go information from it.
func Core(proc *core.Process) (p *Program, err error) {
// Check symbol table to make sure we know the addresses
// of some critical runtime data structures.
m, err := proc.Symbols()
if err != nil {
return nil, err
}
for _, s := range rtSymbols {
if m["runtime."+s.name] == 0 {
// We're missing some address that we need.
return nil, fmt.Errorf("can't find runtime data structure %s. Is the binary unstripped?", s.name)
}
}
// TODO: is the symbol table redundant with the DWARF info? Could we just use DWARF?

// Make sure we have DWARF info.
if _, err := proc.DWARF(); err != nil {
return nil, err
Expand Down Expand Up @@ -50,51 +37,34 @@ func Core(proc *core.Process) (p *Program, err error) {
dwarfMap: map[dwarf.Type]*Type{},
}

// Load the build version.
a := m["runtime.buildVersion"]
ptr := proc.ReadPtr(a)
len := proc.ReadInt(a.Add(proc.PtrSize()))
b := make([]byte, len)
proc.ReadAt(b, ptr)
p.buildVersion = string(b)

// Initialize runtime regions.
p.runtime = map[string]region{}
for _, s := range rtSymbols {
p.runtime[s.name] = region{p: p, a: m["runtime."+s.name], typ: s.typ}
// Initialize everything that just depends on DWARF.
p.readDWARFTypes()
p.readRuntimeConstants()
p.readGlobals()

// Find runtime globals we care about. Initialize regions for them.
p.rtGlobals = map[string]region{}
for _, g := range p.globals {
if strings.HasPrefix(g.Name, "runtime.") {
p.rtGlobals[g.Name[8:]] = region{p: p, a: g.Addr, typ: g.Type}
}
}

p.readDWARFTypes()
p.findRuntimeInfo()
// Read all the data that depends on runtime globals.
p.buildVersion = p.rtGlobals["buildVersion"].String()
p.readModules()
p.readStackVars()
p.readSpans()
p.readMs()
p.readGs()
p.findRoots()
p.readObjects()
p.typeHeap()
return
}

// rtSymbols is a list of all the runtime globals that we need to access,
// together with their types.
var rtSymbols = [...]struct {
name, typ string
}{
{"mheap_", "runtime.mheap"},
{"memstats", "runtime.mstats"},
{"sched", "runtime.schedt"},
{"allfin", "*runtime.finblock"},
{"finq", "*runtime.finblock"},
{"allgs", "[]*runtime.g"},
{"allm", "*runtime.m"},
{"allp", "[1]*p"}, // TODO: type depends on _MaxGomaxprocs
{"modulesSlice", "*[]*runtime.moduledata"},
{"buildVersion", "string"},
return p, nil
}

func (p *Program) readSpans() {
mheap := p.runtime["mheap_"]
mheap := p.rtGlobals["mheap_"]

spanTableStart := mheap.Field("spans").SlicePtr().Address()
spanTableEnd := spanTableStart.Add(mheap.Field("spans").SliceCap() * p.proc.PtrSize())
Expand Down Expand Up @@ -260,14 +230,7 @@ func (p *Program) readSpans() {
}

func (p *Program) readModules() {
// Make a runtime name -> Type map for existing DWARF types.
p.runtimeNameMap = map[string][]*Type{}
for dt, t := range p.dwarfMap {
name := runtimeName(dt)
p.runtimeNameMap[name] = append(p.runtimeNameMap[name], t)
}

ms := p.runtime["modulesSlice"].Deref()
ms := p.rtGlobals["modulesSlice"].Cast("*[]*runtime.moduledata").Deref()
n := ms.SliceLen()
for i := int64(0); i < n; i++ {
md := ms.SliceIndex(i).Deref()
Expand Down Expand Up @@ -343,13 +306,13 @@ func (t *Type) ptrs1(s []int64, off int64) []int64 {
// Convert the address of a runtime._type to a *Type.
// Guaranteed to return a non-nil *Type.
func (p *Program) runtimeType2Type(a core.Address) *Type {
if t, ok := p.runtimeMap[a]; ok {
if t := p.runtimeMap[a]; t != nil {
return t
}
ptrSize := p.proc.PtrSize()

// Read runtime._type.size
r := region{p: p, a: a, typ: "runtime._type"}
r := region{p: p, a: a, typ: p.findType("runtime._type")}
size := int64(r.Field("size").Uintptr())

// Find module this type is in.
Expand Down Expand Up @@ -413,13 +376,12 @@ func (p *Program) runtimeType2Type(a core.Address) *Type {
} else {
// There's no corresponding DWARF type. Make our own.
t = &Type{name: name, Size: size, Kind: KindStruct}
p.types = append(p.types, t)
n := t.Size / ptrSize

// Types to use for ptr/nonptr fields of runtime types which
// have no corresponding DWARF type.
ptr := p.runtimeNameMap["unsafe.Pointer"][0]
nonptr := p.runtimeNameMap["uintptr"][0]
ptr := p.findType("unsafe.Pointer")
nonptr := p.findType("uintptr")
if ptr == nil || nonptr == nil {
panic("ptr / nonptr standins missing")
}
Expand Down Expand Up @@ -457,7 +419,7 @@ func (m *module) readFunc(r region, pcln region) *Func {
f.frameSize.read(r.p.proc, pcln.SliceIndex(int64(r.Field("pcsp").Int32())).a)

// Parse pcdata and funcdata, which are laid out beyond the end of the _func.
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size))
a := r.a.Add(int64(r.p.findType("runtime._func").Size))
n := r.Field("npcdata").Int32()
for i := int32(0); i < n; i++ {
f.pcdata = append(f.pcdata, r.p.proc.ReadInt32(a))
Expand Down Expand Up @@ -485,7 +447,7 @@ func pcdata(r region, pcln region, n int64) core.Address {
if n >= int64(r.Field("npcdata").Int32()) {
return 0
}
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size + 4*n))
a := r.a.Add(int64(r.p.findType("runtime._func").Size + 4*n))

off := r.p.proc.ReadInt32(a)
return pcln.SliceIndex(int64(off)).a
Expand All @@ -501,12 +463,12 @@ func funcdata(r region, n int64) core.Address {
if x&1 != 0 && r.p.proc.PtrSize() == 8 {
x++
}
a := r.a.Add(int64(r.p.rtStructs["runtime._func"].size + 4*int64(x) + r.p.proc.PtrSize()*n))
a := r.a.Add(int64(r.p.findType("runtime._func").Size + 4*int64(x) + r.p.proc.PtrSize()*n))
return r.p.proc.ReadPtr(a)
}

func (p *Program) readMs() {
mp := p.runtime["allm"]
mp := p.rtGlobals["allm"]
for mp.Address() != 0 {
m := mp.Deref()
gs := m.Field("gsignal")
Expand All @@ -521,7 +483,7 @@ func (p *Program) readMs() {

func (p *Program) readGs() {
// TODO: figure out how to "flush" running Gs.
allgs := p.runtime["allgs"]
allgs := p.rtGlobals["allgs"]
n := allgs.SliceLen()
for i := int64(0); i < n; i++ {
r := allgs.SliceIndex(i).Deref()
Expand Down Expand Up @@ -625,7 +587,7 @@ func (p *Program) readFrame(sp, pc core.Address) *Frame {
// Find live ptrs in locals
live := map[core.Address]bool{}
if x := int(p.rtConstants["_FUNCDATA_LocalsPointerMaps"]); x < len(f.funcdata) {
locals := region{p: p, a: f.funcdata[x], typ: "runtime.stackmap"}
locals := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
n := locals.Field("n").Int32() // # of bitmaps
nbit := locals.Field("nbit").Int32() // # of bits per bitmap
idx := f.stackMap.find(off)
Expand All @@ -645,7 +607,7 @@ func (p *Program) readFrame(sp, pc core.Address) *Frame {
}
// Same for args
if x := int(p.rtConstants["_FUNCDATA_ArgsPointerMaps"]); x < len(f.funcdata) {
args := region{p: p, a: f.funcdata[x], typ: "runtime.stackmap"}
args := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
n := args.Field("n").Int32() // # of bitmaps
nbit := args.Field("nbit").Int32() // # of bits per bitmap
idx := f.stackMap.find(off)
Expand Down
Loading

0 comments on commit eabef37

Please sign in to comment.