blob: 93b5451f9a0c708a9503fb288e8432c9910fb6ce [file] [log] [blame]
// 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 (
"fmt"
"sort"
"golang.org/x/debug/core"
)
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
}
func (p *Process) readModules() {
// Note: the cast is necessary for cores generated by Go 1.9, where
// runtime.moduledata is just an unsafe.Pointer.
ms := p.rtGlobals["modulesSlice"].Cast("*[]*runtime.moduledata").Deref()
n := ms.SliceLen()
for i := int64(0); i < n; i++ {
md := ms.SliceIndex(i).Deref()
p.modules = append(p.modules, p.readModule(md))
}
p.funcTab.sort()
}
func (p *Process) readModule(r region) *module {
m := &module{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")
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)
if f.entry != min {
panic(fmt.Errorf("entry %x and min %x don't match for %s", f.entry, min, f.name))
}
p.funcTab.add(min, max, f)
}
return m
}
// 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 {
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)
// 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++ {
f.pcdata = append(f.pcdata, r.p.proc.ReadInt32(a))
a = a.Add(4)
}
a = a.Align(r.p.proc.PtrSize())
n = r.Field("nfuncdata").Int32()
for i := int32(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)
} else {
f.stackMap.setEmpty()
}
return f
}
type funcTabEntry struct {
min, max core.Address
f *Func
}
type funcTab struct {
entries []funcTabEntry
}
// add records that PCs in the range [min,max) map to function f.
func (t *funcTab) add(min, max core.Address, f *Func) {
t.entries = append(t.entries, funcTabEntry{min: min, max: max, f: f})
}
// sort must be called after all the adds, but before any find.
func (t *funcTab) sort() {
sort.Slice(t.entries, func(i, j int) bool {
return t.entries[i].min < t.entries[j].min
})
}
// Finds a Func for the given address. Sort must have been called already.
func (t *funcTab) find(pc core.Address) *Func {
n := sort.Search(len(t.entries), func(i int) bool {
return t.entries[i].max > pc
})
if n == len(t.entries) || pc < t.entries[n].min || pc >= t.entries[n].max {
return nil
}
return t.entries[n].f
}
// a pcTab maps from an offset in a function to an int64.
type pcTab struct {
entries []pcTabEntry
}
type pcTabEntry struct {
bytes int64 // # of bytes this entry covers
val int64 // value over that range of bytes
}
// read parses a pctab from the core file at address data.
func (t *pcTab) read(core *core.Process, data core.Address) {
var pcQuantum int64
switch core.Arch() {
case "386", "amd64", "amd64p32":
pcQuantum = 1
case "s390x":
pcQuantum = 2
case "arm", "arm64", "mips", "mipsle", "mips64", "mips64le", "ppc64", "ppc64le":
pcQuantum = 4
default:
panic("unknown architecture " + core.Arch())
}
val := int64(-1)
first := true
for {
// Advance value.
v, n := readVarint(core, data)
if v == 0 && !first {
return
}
data = data.Add(n)
if v&1 != 0 {
val += ^(v >> 1)
} else {
val += v >> 1
}
// Advance pc.
v, n = readVarint(core, data)
data = data.Add(n)
t.entries = append(t.entries, pcTabEntry{bytes: v * pcQuantum, val: val})
first = false
}
}
func (t *pcTab) setEmpty() {
t.entries = []pcTabEntry{{bytes: 1<<63 - 1, val: -1}}
}
func (t *pcTab) find(off int64) int64 {
for _, e := range t.entries {
if off < e.bytes {
return e.val
}
off -= e.bytes
}
panic("can't find pctab entry")
}
// readVarint reads a varint from the core file.
// val is the value, n is the number of bytes consumed.
func readVarint(core *core.Process, a core.Address) (val, n int64) {
for {
b := core.ReadUint8(a)
val |= int64(b&0x7f) << uint(n*7)
n++
a++
if b&0x80 == 0 {
return
}
}
}