Skip to content

Commit

Permalink
Rearrange finalizer info a bit.
Browse files Browse the repository at this point in the history
Make them regular GC roots.
  • Loading branch information
randall77 committed Oct 4, 2017
1 parent 20bf8f3 commit 23980e0
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 52 deletions.
48 changes: 47 additions & 1 deletion gocore/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,53 @@ func (p *Program) readDWARFTypes() {
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"),
},
},
},
}
}
}

// dwarfSize is used to compute the size of a DWARF type when .Size()
Expand Down Expand Up @@ -790,7 +837,6 @@ func (p *Program) readStackVars() {
}
}
}
// TODO: finalizers, defers
}

/* Dwarf encoding notes
Expand Down
62 changes: 13 additions & 49 deletions gocore/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gocore

import (
"math/bits"
"strings"

"github.com/randall77/corelib/core"
)
Expand Down Expand Up @@ -46,10 +47,14 @@ func (p *Program) readObjects() {
q = append(q, Object(x))
}

// Start with scanning all the roots.
// Note that we don't just use the DWARF roots, just in case DWARF isn't complete.
// Instead we use exactly what the runtime uses.

// Goroutine roots
for _, g := range p.goroutines {
for _, f := range g.frames {
for a := range f.Live { // TODO: iteration order matter?
for a := range f.Live {
add(p.proc.ReadPtr(a))
}
}
Expand All @@ -71,38 +76,18 @@ func (p *Program) readObjects() {
}

// Finalizers
mheap := p.rtGlobals["mheap_"]
allspans := mheap.Field("allspans")
nSpan := allspans.SliceLen()
for i := int64(0); i < nSpan; i++ {
s := allspans.SliceIndex(i).Deref()
for sp := s.Field("specials"); sp.Address() != 0; sp = sp.Field("next") {
sp = sp.Deref() // *special to special
if sp.Field("kind").Uint8() != uint8(p.rtConstants["_KindSpecialFinalizer"]) {
// All other specials (just profile records) are not stored in the heap.
continue
for _, r := range p.globals {
if !strings.HasPrefix(r.Name, "finalizer for ") {
continue
}
for _, f := range r.Type.Fields {
if f.Type.Kind == KindPtr {
add(p.proc.ReadPtr(r.Addr.Add(f.Off)))
}
// Note: the type runtime.specialfinalizer is the type here, but
// that type doesn't make it into the DWARF info. So we have to
// manually compute offsets.
// type specialfinalizer struct {
// special special
// fn *funcval
// nret uintptr
// fint *_type
// ot *ptrtype
// }
a := sp.a.Add(p.findType("runtime.special").Size)
add(p.proc.ReadPtr(a.Add(0 * p.proc.PtrSize())))
add(p.proc.ReadPtr(a.Add(2 * p.proc.PtrSize())))
add(p.proc.ReadPtr(a.Add(3 * p.proc.PtrSize())))

// TODO: record these somewhere so ForEachPtr can return them.
}
}

// Expand root set to all reachable objects.
// TODO: run in parallel?
for len(q) > 0 {
x := q[len(q)-1]
q = q[:len(q)-1]
Expand Down Expand Up @@ -334,24 +319,3 @@ func edges1(p *Program, r *Root, off int64, t *Type, fn func(int64, Object, int6
}
return true
}

// A revEdge is an incoming edge to an object.
// Exactly one of fromObj or fromRoot will be non-nil.
// fromIdx is the offset in fromObj/fromRoot where the pointer was found.
// toIdx is the offset in the pointed-to object where the pointer lands.
type revEdge struct {
fromObj Object
fromRoot *Root
fromIdx int64
toIdx int64 // TODO: compute when needed?
}

// ForEachIncomingPtr calls fn for all incoming pointers into object x.
// It calls fn with:
// the object or root containing the pointer (exactly one will be non-nil)
// the offset in the object/root where the pointer is found
// the offset of the target of the edge in x.
// If fn returns false, ForEachIncomingPtr returns immediately.
func (p *Program) ForEachIncomingPtr(x Object, fn func(Object, *Root, int64, int64) bool) {
//TODO
}
21 changes: 21 additions & 0 deletions gocore/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ func (p *Program) readSpans() {
p.heapInfo[(a.Sub(p.arenaStart))/512] = heapInfo{base: min, size: elemSize, firstIdx: -1}
}

// Process special records.
for sp := s.Field("specials"); sp.Address() != 0; sp = sp.Field("next") {
sp = sp.Deref() // *special to special
if sp.Field("kind").Uint8() != uint8(p.rtConstants["_KindSpecialFinalizer"]) {
// All other specials (just profile records) can't point into the heap.
continue
}
obj := min.Add(int64(sp.Field("offset").Uint16()))
p.globals = append(p.globals,
&Root{
Name: fmt.Sprintf("finalizer for %x", obj),
Addr: sp.a,
Type: p.findType("runtime.specialfinalizer"),
Frame: nil,
})
// TODO: these aren't really "globals", as they
// are kept alive by the object they reference being alive.
// But we have no way of adding edges from an object to
// the corresponding finalizer data, so we punt on that thorny
// issue for now.
}
case spanFree:
freeSpanSize += spanSize
case spanDead:
Expand Down
13 changes: 11 additions & 2 deletions gocore/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,17 @@ func (r region) Int32() int32 {
return r.p.proc.ReadInt32(r.a)
}

// Uint64 returns the uint64 value stored in r.
// r must have type uint64 or uintptr (on a 64-bit machine).
// Uint16 returns the uint16 value stored in r.
// r must have type uint16.
func (r region) Uint16() uint16 {
if r.typ.Kind != KindUint || r.typ.Size != 2 {
panic("bad uint16 type " + r.typ.name)
}
return r.p.proc.ReadUint16(r.a)
}

// Uint8 returns the uint8 value stored in r.
// r must have type uint8.
func (r region) Uint8() uint8 {
if r.typ.Kind != KindUint || r.typ.Size != 1 {
panic("bad uint8 type " + r.typ.name)
Expand Down

0 comments on commit 23980e0

Please sign in to comment.