blob: 8d4ac99d52493279f71c3274759b0eaa580a5806 [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 (
"encoding/binary"
"unsafe"
"golang.org/x/debug/internal/core"
)
// A Root is an area of memory that might have pointers into the Go heap.
type Root struct {
Name string
Type *Type // always non-nil
// Frame, if non-nil, points to the frame in which this root lives.
// Roots with non-nil Frame fields refer to local variables on a stack.
// A stack root might be a large type, with some of its fields live and
// others dead. Consult Frame.Live to find out which pointers in a stack
// root are live.
Frame *Frame
pieces []rootPiece
id int
}
// HasAddress returns true if the root is simple and contiguous, and can be
// described with just a single address.
func (r *Root) HasAddress() bool {
return len(r.pieces) == 1 && r.pieces[0].kind == addrPiece
}
// Addr returns the address of the root, if it has one.
func (r *Root) Addr() core.Address {
if r.HasAddress() {
return core.Address(r.pieces[0].value)
}
return 0
}
func (p *Process) makeMemRoot(name string, typ *Type, fr *Frame, addr core.Address) *Root {
return makeMemRoot(&p.nRoots, name, typ, fr, addr)
}
func makeMemRoot(nRoots *int, name string, typ *Type, fr *Frame, addr core.Address) *Root {
r := &Root{
Name: name,
Type: typ,
Frame: fr,
pieces: []rootPiece{{size: typ.Size, kind: addrPiece, value: uint64(addr)}},
id: *nRoots,
}
*nRoots += 1
return r
}
func (p *Process) makeCompositeRoot(name string, typ *Type, fr *Frame, pieces []rootPiece) *Root {
r := &Root{
Name: name,
Type: typ,
Frame: fr,
pieces: pieces,
id: p.nRoots,
}
p.nRoots++
return r
}
func (pr *Process) readRootPtr(r *Root, offset int64) core.Address {
// TODO(mknyszek): Little-endian only.
ptrBuf := make([]byte, pr.proc.PtrSize())
pr.readRootAt(r, ptrBuf, offset)
if pr.proc.PtrSize() == 4 {
return core.Address(binary.LittleEndian.Uint32(ptrBuf))
}
return core.Address(binary.LittleEndian.Uint64(ptrBuf))
}
// ReadRootAt reads data out of this root. offset+len(b) must be less than r.Type.Size.
// Returns the address read from, if the read was contiguous and from memory.
func (pr *Process) readRootAt(r *Root, b []byte, offset int64) core.Address {
if offset+int64(len(b)) > r.Type.Size {
panic("invalid range to read from root")
}
if len(b) == 0 {
return 0
}
bOff := int64(0)
var addr core.Address
first := true
for _, p := range r.pieces {
if offset > p.off+p.size {
continue
}
if offset+int64(len(b)) <= p.off {
break
}
pOff := max(p.off, offset)
base := pOff - p.off
rlen := min(int64(len(b))-bOff, p.size-base)
switch p.kind {
case addrPiece:
pr.proc.ReadAt(b[bOff:bOff+rlen], core.Address(p.value).Add(base))
if first {
addr = core.Address(p.value).Add(base)
} else {
addr = 0
}
case regPiece, immPiece:
// TODO(mknyszek): Supports little-endian only.
v := ((*[8]byte)(unsafe.Pointer(&p.value)))[:p.size]
copy(b[bOff:bOff+rlen], v[base:base+rlen])
addr = 0
}
if first {
first = false
}
bOff += rlen
if bOff == int64(len(b)) {
break
}
}
return addr
}
// walkRootTypePtrs calls fn for the edges found in an object of type t living at offset off in the root r.
// If fn returns false, return immediately with false.
func walkRootTypePtrs(p *Process, r *Root, ptrBuf []byte, off int64, t *Type, fn func(int64, Object, int64) bool) bool {
switch t.Kind {
case KindBool, KindInt, KindUint, KindFloat, KindComplex:
// no edges here
case KindIface, KindEface:
// The first word is a type or itab.
// Itabs are never in the heap.
// Types might be, though.
// We have no idea about the liveness of registers, when a == 0.
a := p.readRootAt(r, ptrBuf[:p.proc.PtrSize()], off)
if a != 0 && (r.Frame == nil || r.Frame.Live[a]) {
var ptr core.Address
if p.proc.PtrSize() == 4 {
ptr = core.Address(binary.LittleEndian.Uint32(ptrBuf[:]))
} else {
ptr = core.Address(binary.LittleEndian.Uint64(ptrBuf[:]))
}
dst, off2 := p.FindObject(ptr)
if dst != 0 {
if !fn(off, dst, off2) {
return false
}
}
}
// Treat second word like a pointer.
off += p.proc.PtrSize()
fallthrough
case KindPtr, KindString, KindSlice, KindFunc:
a := p.readRootAt(r, ptrBuf[:p.proc.PtrSize()], off)
if a != 0 && (r.Frame == nil || r.Frame.Live[a]) {
var ptr core.Address
if p.proc.PtrSize() == 4 {
ptr = core.Address(binary.LittleEndian.Uint32(ptrBuf[:]))
} else {
ptr = core.Address(binary.LittleEndian.Uint64(ptrBuf[:]))
}
dst, off2 := p.FindObject(ptr)
if dst != 0 {
if !fn(off, dst, off2) {
return false
}
}
}
case KindArray:
s := t.Elem.Size
for i := int64(0); i < t.Count; i++ {
if !walkRootTypePtrs(p, r, ptrBuf, off+i*s, t.Elem, fn) {
return false
}
}
case KindStruct:
for _, f := range t.Fields {
if !walkRootTypePtrs(p, r, ptrBuf, off+f.Off, f.Type, fn) {
return false
}
}
}
return true
}
type rootPieceKind int
const (
addrPiece rootPieceKind = iota
regPiece
immPiece
)
type rootPiece struct {
off int64 // Logical offset into the root, or specifically the root's Type.
size int64 // Size of the piece.
kind rootPieceKind // Where the piece is.
value uint64 // Address if kind == AddrPiece, value if kind == RegPiece or kind == ImmPiece.
}