2.5 KiB
flamegraph
(no submodules)
Purpose
FlameGraphGenerator builds a flame-graph data structure from ProfileEntry arrays by nesting entries on start/end-time containment. The output is a FlameNode tree compatible with d3-flame-graph and similar visualisers; SVG/HTML rendering is intentionally out of scope (a downstream concern), but toD3Format produces the exact JSON shape these libraries expect.
Features
- Pure containment algorithm: an entry whose
[startTime, endTime]interval is fully inside another's becomes a child of it. - Tie-breaking on equal start times: wider spans first so they own the narrower children.
- Self-time computation:
selfTime = value - sum(children.value), never negative. collapseRecursive(node)merges consecutive same-name parent/child pairs, useful for cleaning up tail-recursive call patterns before rendering.toD3Format(node)strips internal fields (selfTime,startTime,endTime) so the JSON is exactly{ name, value, children }.
Usage: Basic
import { FlameGraphGenerator } from "@suaveplan/profiler";
const gen = new FlameGraphGenerator();
const tree = gen.generate(profiler.getEntries());
const d3Json = gen.toD3Format(tree);
Usage: Advanced
import { FlameGraphGenerator } from "@suaveplan/profiler";
const gen = new FlameGraphGenerator();
const rawTree = gen.generate(entries);
const collapsed = gen.collapseRecursive(rawTree);
// rawTree shows the full recursion stack; collapsed flattens
// `parser → parser → parser → leaf` into `parser → leaf`.
const json = gen.toD3Format(collapsed);
await writeFile("flame.json", JSON.stringify(json));
API Reference
class FlameGraphGenerator— no constructor args (stateless).- Methods:
generate(entries: readonly ProfileEntry[]): FlameNodetoD3Format(node: FlameNode): { name, value, children }collapseRecursive(node: FlameNode): FlameNode
Implementation Notes
The internal MutableFlameNode carries startTime/endTime for the duration of tree construction so containment checks remain O(1) per node-pair; the final freeze step drops these fields and returns the immutable FlameNode shape. When a new entry is inserted, the algorithm first checks for a containing existing child (recursing into it) and otherwise creates a new node, absorbing any existing siblings the new entry contains. This handles arbitrary insertion order — the input is sorted by start-time ascending, ties broken by wider span first, before insertion starts.