snapshotgraph

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

Documentation

Overview

Package snapshotgraph turns a parsed heapsnapshot.HeapSnapshot into a compact process-wide object graph with per-goroutine and process-global reachability sets.

The package does not depend on Delve and does not parse heap dumps itself. It only resolves raw pointer addresses produced by the heap dump parser into object IDs, builds graph edges, and walks the graph.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComputeReachability

func ComputeReachability(a *Analysis)

ComputeReachability walks the graph from every goroutine's roots and from the global roots, filling GoroutineReachability.Reachable, GlobalReachability.Reachable, and the reach-derived counters in Stats (GoroutineReachableObjects, GlobalReachableObjects, SharedByGoroutinesObjects, UnreachableObjects).

ComputeReachability is optional: Build no longer calls it. Callers that need whole-process reachability (diagnostics, offline analysis) should run it immediately after Build. The /debug/memusage handler skips this in favour of a single union BFS over only the goroutines whose labels match the request selector.

Idempotent: running ComputeReachability twice yields the same sets and stats.

func ReachableFrom

func ReachableFrom(g *Graph, roots []RootRef) map[ObjectID]struct{}

ReachableFrom walks the graph iteratively starting from each root and returns the set of object IDs reachable from any of them. It handles cycles, self-edges, and shared objects. Invalid object IDs in roots or in Children edges are skipped rather than panicking.

Types

type Analysis

type Analysis struct {
	Graph      *Graph
	Goroutines []GoroutineReachability
	Globals    GlobalReachability

	Warnings []string
	Stats    Stats
}

Analysis is the full output of Build: a resolved object graph plus reachability sets and summary stats.

func Build

func Build(snap *heapsnapshot.HeapSnapshot, opts Options) (*Analysis, error)

Build converts a parsed HeapSnapshot into an Analysis. It resolves pointer values to object IDs (supporting interior pointers), builds graph edges with deduplication, and extracts goroutine and global roots.

Build is structural only: it does not walk the graph. Reachability sets (GoroutineReachability.Reachable, GlobalReachability.Reachable) and reach-derived Stats (GoroutineReachableObjects, GlobalReachableObjects, SharedByGoroutinesObjects, UnreachableObjects) are populated by ComputeReachability. Callers that need whole-process reachability should call ComputeReachability immediately after Build. Callers that only need reachability for a label-selected subset of goroutines (the /debug/memusage handler) should skip ComputeReachability and traverse from the union of matched goroutine roots instead, paying for one BFS rather than one per goroutine plus globals.

Parser-level warnings from snap.Warnings are forwarded to Analysis.Warnings with a "parse: " prefix so downstream consumers see every recoverable problem in one place.

type GlobalReachability

type GlobalReachability struct {
	Roots     []RootRef
	Reachable map[ObjectID]struct{}
}

GlobalReachability is the reachability set rooted at process-wide roots: data, bss, otherroot, finalizer, queued_finalizer.

Reachable is populated by ComputeReachability, not Build.

type GoroutineReachability

type GoroutineReachability struct {
	GoroutineID  uint64
	IsSystem     bool
	IsBackground bool
	Roots        []RootRef
	Reachable    map[ObjectID]struct{}
}

GoroutineReachability is the reachability set rooted at a single goroutine's stack roots.

IsSystem and IsBackground are forwarded from the parsed goroutine record. Runtime-internal goroutines (g0, GC workers, the finalizer goroutine, etc.) hold pointers into runtime metadata that can pollute per-goroutine attribution if folded into user-visible bubbles. Phase 4 keeps them in the analysis but tags them so later phases can filter.

Reachable is populated by ComputeReachability, not Build. Callers that only need a label-selected subset (e.g. /debug/memusage) can ignore it and walk the graph themselves from Roots.

type Graph

type Graph struct {
	Objects []Object
	ByAddr  map[uint64]ObjectID
	// contains filtered or unexported fields
}

Graph holds the compact process-wide object graph.

func (*Graph) FindObjectContaining

func (g *Graph) FindObjectContaining(ptr uint64) (ObjectID, bool)

FindObjectContaining resolves a non-zero pointer value to the object that contains it. The lookup supports interior pointers: a pointer somewhere inside an object's payload still resolves to that object. Address zero is treated as nil and never resolves.

type Object

type Object struct {
	ID       ObjectID
	Addr     uint64
	Size     uint64
	Children []ObjectID
}

Object is one heap object in the resolved graph.

type ObjectID

type ObjectID uint32

ObjectID identifies one object in a Graph. It is also the object's index in Graph.Objects.

type ObjectRange

type ObjectRange struct {
	Start uint64
	End   uint64
	ID    ObjectID
}

ObjectRange is one half-open address range [Start, End) covered by a single object. Used for interior-pointer resolution. Zero-sized objects are intentionally not registered as ranges.

type Options

type Options struct {
	Strict bool
}

Options tunes the builder. Strict turns warnings into errors.

type RootRef

type RootRef struct {
	ObjectID ObjectID
	Ptr      uint64
	SlotAddr uint64
	Kind     string
	Detail   string
}

RootRef is one resolved pointer from a stack frame, global root, or finalizer record into a heap object.

type Stats

type Stats struct {
	Objects                int
	ObjectBytes            uint64
	Edges                  int
	RawObjectPointers      int
	ZeroObjectPointers     int
	ResolvedObjectPointers int

	UnresolvedPointers       int
	UnresolvedObjectPointers int
	UnresolvedGoroutineRoots int
	UnresolvedGlobalRoots    int
	UnresolvedFinalizerRoots int

	Goroutines            int
	SystemGoroutines      int
	GoroutineRootPointers int
	GlobalRoots           int

	GoroutineReachableObjects int
	GlobalReachableObjects    int
	UnreachableObjects        int

	SharedByGoroutinesObjects int
}

Stats aggregates counters across the analysis.

Pointer accounting (object pointer slots):

RawObjectPointers       = total pointer values seen in object slots,
                          including zero pointers
ZeroObjectPointers      = how many of those were zero (nil)
ResolvedObjectPointers  = non-zero pointers that resolved to a heap
                          object (pre-dedup)
UnresolvedObjectPointers = non-zero pointers that did not resolve
Edges                   = deduplicated child edges in the graph

Invariant:

RawObjectPointers = ZeroObjectPointers + ResolvedObjectPointers + UnresolvedObjectPointers

(Iface/eface data-word edges are decoded at the parser level — see ParseStats.InterfaceFieldsDecoded / EfaceFieldsDecoded — and included in PointerAddrs before the graph builder runs, so they contribute to RawObjectPointers. For current Go runtime versions these counts are always zero because the dump writer only emits fieldKindPtr from GC bitmaps.)

Root accounting tracks unresolved root pointers separately by source category so it is clear whether missing reachability is due to objects, goroutine stacks, globals, or finalizers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL