blob: 43e0e50dac2c84c7f51a543cb8325c83a11e3b2b [file]
// 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 (
"iter"
)
func (p *Process) reverseEdges() {
p.initReverseEdges.Do(func() {
// First, count the number of edges into each object.
// This allows for efficient packing of the reverse edge storage.
cnt := make([]int64, p.nObj+1)
p.ForEachObject(func(x Object) bool {
p.ForEachPtr(x, func(_ int64, y Object, _ int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
cnt[idx]++
return true
})
return true
})
p.ForEachRoot(func(r *Root) bool {
p.ForEachRootPtr(r, func(_ int64, y Object, _ int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
cnt[idx]++
return true
})
return true
})
// Compute cumulative count of all incoming edges up to and including each object.
var n int64
for idx, c := range cnt {
n += c
cnt[idx] = n
}
// Allocate all the storage for the reverse edges.
p.redge = make([]reverseEdge, n)
// Add edges to the lists.
p.ForEachObject(func(x Object) bool {
p.ForEachPtr(x, func(i int64, y Object, _ int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
e := cnt[idx]
e--
cnt[idx] = e
p.redge[e] = reverseEdge{addr: p.Addr(x).Add(i)}
return true
})
return true
})
p.ForEachRoot(func(r *Root) bool {
p.ForEachRootPtr(r, func(i int64, y Object, _ int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
e := cnt[idx]
e--
cnt[idx] = e
p.redge[e] = reverseEdge{root: r, rOff: i}
return true
})
return true
})
// At this point, cnt contains the cumulative count of all edges up to
// but *not* including each object.
p.ridx = cnt
// Make root index.
p.rootIdx = make([]*Root, p.nRoots)
p.ForEachRoot(func(r *Root) bool {
p.rootIdx[r.id] = r
return true
})
})
}
// ForEachReversePtr calls fn for all pointers it finds pointing to y.
// It calls fn with:
//
// the object or root which points to y (exactly one will be non-nil)
// the offset i in that object or root where the pointer appears.
// the offset j in y where the pointer points.
//
// If fn returns false, ForEachReversePtr returns immediately.
func (p *Process) ForEachReversePtr(y Object, fn func(x Object, r *Root, i, j int64) bool) {
p.reverseEdges()
idx, _ := p.findObjectIndex(p.Addr(y))
for _, a := range p.redge[p.ridx[idx]:p.ridx[idx+1]] {
if a.root != nil {
ptr, _ := p.readRootPtr(a.root, a.rOff)
if !fn(0, a.root, a.rOff, ptr.Sub(p.Addr(y))) {
return
}
} else {
// Read pointer, compute offset in y.
ptr := p.proc.ReadPtr(a.addr)
j := ptr.Sub(p.Addr(y))
// Find source of pointer.
x, i := p.FindObject(a.addr)
if x != 0 {
// Source is an object.
if !fn(x, nil, i, j) {
return
}
}
}
}
}
// A Link represents a pointer to [Link.Dst].
//
// [Link.DstOff] is non-zero if the pointer is an interior pointer.
//
// [Link.SrcOff] is non-zero if the location of the pointer itself is in the
// interior of the source. The source object itself is not in the Link struct.
// See either the previous Link or the [Root].
//
// An example, assuming objects "src" and "dst" exist:
//
// src{
// a // ...
// b // ...
// ptr *someType
// }
//
// dst{
// c // ...
// d // ...
// someType // ...
// }
//
// Then the Link representing the connection will be
//
// Link{
// SrcOff: &src.ptr - &src,
// Dst: &dst,
// DstOff: &dst.someType - &dst,
// }
//
// TODO(aktau): How are Roots that _are_ the object we're looking for themselves
// materialized?
type Link struct {
Dst Object // Object containing the pointed-to address.
SrcOff int64 // Offset in the source [Object] or [Root] where this pointer was found. The location of the pointer is p.Addr(source).Add(SrcOff).
DstOff int64 // Offset into Dst. The pointed-to address is p.Addr(Dst).Add(DstOff).
}
// Reachable returns an iterator that yields a path (slice of [Link]s) for every
// [Root] that refers to obj.
//
// The order in which roots are returned is undefined but deterministic.
// Similarly for multiple paths out of a given root: a shortest path is
// returned. Which one is undefined but deterministic.
//
// The link slice always contains at least one element, representing the link
// between the root and the object.
//
// The link slice (path) is invalidated on every iteration. The elements are
// values, so (e.g.) [slices.Clone] can be used to retain the path.
func (p *Process) Reachable(obj Object) iter.Seq2[*Root, []Link] {
return func(yield func(*Root, []Link) bool) {
depth := map[Object]int{}
depth[obj] = 0
var path []Link // Path slice is reused over all returned elements.
q := []Object{obj}
var stop bool
for len(q) > 0 && !stop {
// Follow the reverse pointers Depth-First (FIFO).
y := q[0]
q = q[1:]
p.ForEachReversePtr(y, func(x Object, r *Root, i, j int64) bool {
if r != nil {
// Found a root. Reconstruct a path.
path = path[:0] // Re-use memory.
path = append(path, Link{
SrcOff: i, // i: the offset in [r] where the pointer resides.
Dst: y,
DstOff: j, // j: the offset in [y] where the pointer points.
})
// Object hierarchy.
for z := y; z != obj; {
// Find an edge out of z which goes to an object closer to obj.
p.ForEachPtr(z, func(i int64, w Object, j int64) bool {
if d, ok := depth[w]; ok && d < depth[z] {
path = append(path, Link{
SrcOff: i, // the offset in [z] where the pointer resides.
Dst: w,
DstOff: j, // the offset in [w] where the pointer points.
})
z = w
return false
}
return true
})
}
if !yield(r, path) {
stop = true
return false
}
return true
}
// Reverse pointer to a non-root. Add this object to the work list
// (unless we've already seen it).
if _, ok := depth[x]; ok {
return true
}
depth[x] = depth[y] + 1
q = append(q, x)
return true
})
}
}
}