Skip to content

gocore: bugfix to Go 1.17 cores, Go 1.16 cores and Go 1.11+ cores #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
6 changes: 3 additions & 3 deletions cmd/viewcore/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,14 @@ func runRoot(cmd *cobra.Command, args []string) {
cmd.Usage()
return
}
// Interactive mode.
cfg.interactive = true

p, _, err := readCore()
if err != nil {
exitf("%v\n", err)
}

// Interactive mode.
cfg.interactive = true

// Create a dummy root to run in shell.
root := &cobra.Command{}
// Make all subcommands of viewcore available in the shell.
Expand Down
18 changes: 18 additions & 0 deletions internal/gocore/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gocore
import (
"debug/dwarf"
"fmt"
"reflect"
"regexp"
"sort"
"strings"
Expand Down Expand Up @@ -58,6 +59,23 @@ func (p *Process) readDWARFTypes() {
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.
Expand Down
2 changes: 2 additions & 0 deletions internal/gocore/gocore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,6 @@ func TestVersions(t *testing.T) {
loadExampleVersion(t, "1.13.zip")
loadExampleVersion(t, "1.13.3.zip")
loadExampleVersion(t, "1.14.zip")
loadExampleVersion(t, "1.16.zip")
loadExampleVersion(t, "1.17.zip")
}
56 changes: 43 additions & 13 deletions internal/gocore/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type module struct {
r region // inferior region holding a runtime.moduledata
types, etypes core.Address // range that holds all the runtime._type data in this module
p *Process // The parent process of this module.
}

func (p *Process) readModules() {
Expand All @@ -29,20 +30,31 @@ func (p *Process) readModules() {
}

func (p *Process) readModule(r region) *module {
m := &module{r: r}
m := &module{p: p, r: r}
m.types = core.Address(r.Field("types").Uintptr())
m.etypes = core.Address(r.Field("etypes").Uintptr())

// Read the pc->function table
pcln := r.Field("pclntable")
var pctab, funcnametab region
if p.minorVersion >= 16 {
// In 1.16, pclntable was split up into pctab and funcnametab.
pctab = r.Field("pctab")
funcnametab = r.Field("funcnametab")
}
ftab := r.Field("ftab")
n := ftab.SliceLen() - 1 // last slot is a dummy, just holds entry
for i := int64(0); i < n; i++ {
ft := ftab.SliceIndex(i)
min := core.Address(ft.Field("entry").Uintptr())
max := core.Address(ftab.SliceIndex(i + 1).Field("entry").Uintptr())
fr := pcln.SliceIndex(int64(ft.Field("funcoff").Uintptr())).Cast("runtime._func")
f := m.readFunc(fr, pcln)
var f *Func
if p.minorVersion >= 16 {
f = m.readFunc(fr, pctab, funcnametab)
} else {
f = m.readFunc(fr, pcln, pcln)
}
if f.entry != min {
panic(fmt.Errorf("entry %x and min %x don't match for %s", f.entry, min, f.name))
}
Expand All @@ -55,34 +67,52 @@ func (p *Process) readModule(r region) *module {
// readFunc parses a runtime._func and returns a *Func.
// r must have type runtime._func.
// pcln must have type []byte and represent the module's pcln table region.
func (m *module) readFunc(r region, pcln region) *Func {
func (m *module) readFunc(r region, pctab region, funcnametab region) *Func {
f := &Func{module: m, r: r}
f.entry = core.Address(r.Field("entry").Uintptr())
f.name = r.p.proc.ReadCString(pcln.SliceIndex(int64(r.Field("nameoff").Int32())).a)
f.frameSize.read(r.p.proc, pcln.SliceIndex(int64(r.Field("pcsp").Int32())).a)
f.name = r.p.proc.ReadCString(funcnametab.SliceIndex(int64(r.Field("nameoff").Int32())).a)
pcsp := r.Field("pcsp")
var pcspIdx int64
if m.p.minorVersion >= 16 {
// In 1.16, pcsp changed to be a uint32 from an int32.
pcspIdx = int64(pcsp.Uint32())
} else {
pcspIdx = int64(pcsp.Int32())
}
f.frameSize.read(r.p.proc, pctab.SliceIndex(pcspIdx).a)

// Parse pcdata and funcdata, which are laid out beyond the end of the _func.
a := r.a.Add(int64(r.p.findType("runtime._func").Size))
n := r.Field("npcdata").Int32()
for i := int32(0); i < n; i++ {
// In 1.16, npcdata changed to be a uint32 from an int32.
npcdata := r.Field("npcdata")
var n uint32
if m.p.minorVersion >= 16 {
// In 1.16, pcsp changed to be a uint32 from an int32.
n = uint32(npcdata.Uint32())
} else {
n = uint32(npcdata.Int32())
}
nfd := r.Field("nfuncdata")
a := nfd.a.Add(nfd.typ.Size)

for i := uint32(0); i < n; i++ {
f.pcdata = append(f.pcdata, r.p.proc.ReadInt32(a))
a = a.Add(4)
}
a = a.Align(r.p.proc.PtrSize())

if nfd := r.Field("nfuncdata"); nfd.typ.Size == 1 { // go 1.12 and beyond, this is a uint8
n = int32(nfd.Uint8())
if nfd.typ.Size == 1 { // go 1.12 and beyond, this is a uint8
n = uint32(nfd.Uint8())
} else { // go 1.11 and earlier, this is an int32
n = nfd.Int32()
n = uint32(nfd.Int32())
}
for i := int32(0); i < n; i++ {
for i := uint32(0); i < n; i++ {
f.funcdata = append(f.funcdata, r.p.proc.ReadPtr(a))
a = a.Add(r.p.proc.PtrSize())
}

// Read pcln tables we need.
if stackmap := int(r.p.rtConstants["_PCDATA_StackMapIndex"]); stackmap < len(f.pcdata) {
f.stackMap.read(r.p.proc, pcln.SliceIndex(int64(f.pcdata[stackmap])).a)
f.stackMap.read(r.p.proc, pctab.SliceIndex(int64(f.pcdata[stackmap])).a)
} else {
f.stackMap.setEmpty()
}
Expand Down
26 changes: 23 additions & 3 deletions internal/gocore/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"debug/dwarf"
"fmt"
"math/bits"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -54,6 +55,7 @@ type Process struct {
stats *Stats

buildVersion string
minorVersion int

globals []*Root

Expand Down Expand Up @@ -153,6 +155,15 @@ func Core(proc *core.Process) (p *Process, err error) {

// Read all the data that depend on runtime globals.
p.buildVersion = p.rtGlobals["buildVersion"].String()
versionComponents := strings.Split(p.buildVersion, ".")
if len(versionComponents) < 2 {
panic("malformed version " + p.buildVersion)
}
p.minorVersion, err = strconv.Atoi(versionComponents[1])
if err != nil {
panic("malformed version " + p.buildVersion)
}

p.readModules()
p.readHeap()
p.readGs()
Expand Down Expand Up @@ -258,10 +269,16 @@ func (p *Process) readHeap() {
// Copy out ptr/nonptr bits
n := bitmap.ArrayLen()
for i := int64(0); i < n; i++ {
// The nth byte is composed of 4 object bits and 4 live/dead
// bits. We ignore the 4 live/dead bits, which are on the
// high order side of the byte.
//
// See mbitmap.go for more information on the format of
// the bitmap field of heapArena.
m := bitmap.ArrayIndex(i).Uint8()
for j := int64(0); j < 8; j++ {
for j := int64(0); j < 4; j++ {
if m>>uint(j)&1 != 0 {
p.setHeapPtr(min.Add((i*8 + j) * ptrSize))
p.setHeapPtr(min.Add((i*4 + j) * ptrSize))
}
}
}
Expand Down Expand Up @@ -316,7 +333,10 @@ func (p *Process) readSpans(mheap region, arenas []arena) {
panic("weird mapping " + m.Perm().String())
}
}
if mheap.HasField("curArena") { // go1.13.3 and up
if p.minorVersion < 17 && mheap.HasField("curArena") {
// go1.13.3 and up has curArena.
// In Go 1.17, we ... don't need to do this any longer. See patch
// bd6aeca9686d5e672ffda1ea0cfeac7a3e7a20a4
// Subtract from the heap unallocated space
// in the current arena.
ca := mheap.Field("curArena")
Expand Down
Binary file added internal/gocore/testdata/1.16.zip
Binary file not shown.
Binary file added internal/gocore/testdata/1.17.zip
Binary file not shown.
5 changes: 5 additions & 0 deletions internal/gocore/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ func (p *Process) typeHeap() {
for len(work) > 0 {
c := work[len(work)-1]
work = work[:len(work)-1]
switch c.t.Kind {
case KindBool, KindInt, KindUint, KindFloat, KindComplex:
// Don't do O(n) function calls for big primitive slices
continue
}
for i := int64(0); i < c.r; i++ {
p.typeObject(c.a.Add(i*c.t.Size), c.t, p.proc, add)
}
Expand Down