Files
skills/docs/devtools/tooling/profiler/src/flamegraph/flamegraph.md
2026-05-29 08:20:56 +01:00

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[]): FlameNode
    • toD3Format(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.