in internal/gocore/dwarf.go [462:604]
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)
}
}
}
}