Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/clean-hotspot-attribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@lanterna-profiler/cli": minor
"@lanterna-profiler/core": minor
"@lanterna-profiler/detectors": minor
---

Improve hotspot attribution diagnostics and agent report readability.

Lanterna now preserves anonymous user-code wrapper frames as actionable attribution clues, carries richer user-caller evidence through memory, async, GC, and CPU findings, and clarifies internal hotspot attribution naming without changing the public CLI contract.
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.14/schema.json",

Check notice on line 2 in biome.json

View workflow job for this annotation

GitHub Actions / Quality (lint + format)

deserialize

The configuration schema version does not match the CLI version 2.4.15
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
8 changes: 8 additions & 0 deletions docs/reading-a-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ When `measurementBasis === "histogram"`, `correlatedHotspots[]` is based on over

Fires only when a function is **both** hot in the CPU profile **and** repeatedly deoptimised under `--deep`. Focus on stabilising shapes and types, then reprofile. One-off deopt entries are noise.

#### Memory trend findings and `correlatedAllocator`

`memory-growth:*` and `external-buffer-pressure` are process-level heuristics, so `evidence.file` stays on `process.memoryUsage`. When available, `evidence.extra.correlatedAllocator` is the allocator lead to inspect first; prefer its `userCaller` when present. `basis: heap-sampled-allocator` comes from the V8 heap sampler, while `basis: cpu-top-user-hotspot` is used for off-heap pressure when a CPU profile was captured.

#### Async findings and `userCaller`

Async findings can expose `evidence.extra.userCaller` from async init stacks or CPU-window attribution. For `hot-async-context:<id>`, `evidence.*` remains the hot execution frame, while `evidence.extra.entryFrame` names the async entry point that drove the chain.

#### `node-modules-hotspot:<package>`

A dependency hotspot is often a symptom — your code controls when and how often the dependency runs. Inspect the caller path, reduce input size or call frequency, and only then decide whether the dependency itself needs replacing.
Expand Down
13 changes: 11 additions & 2 deletions docs/report-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ When present, prefer `source.file:source.line` for human diagnosis and patching,

`source?: SourceLocation` can appear on CPU hotspots, hot-stack frames and anchors, memory allocators and memory summaries, async frame-bearing entries, deopts, and `findings[].evidence`.

`userCaller?: UserCallerAttribution` can appear when the visible cost is outside user code but Lanterna can identify the nearest user frame that led there. It contains `function`, `file`, `line`, optional `column`/`source`/`stackDistance`, `profilePct`, `supportPct`, `confidence` (`low`/`medium`/`high`), and `basis` (`cpu-sample-path`, `heap-sample-path`, `async-stack`, or `async-cpu-window`). `stackDistance: 1` means the closest user frame to the external callee. Attributed findings may also expose `evidence.extra.candidateCallers[]`, ordered by proximity first and support second. Treat low-confidence attribution as an inspection lead, not automatically as the line to patch.
`userCaller?: UserCallerAttribution` can appear when Lanterna can identify the user frame that explains a finding. It contains `function`, `file`, `line`, optional `column`/`source`/`stackDistance`, `profilePct`, `supportPct`, `confidence` (`low`/`medium`/`high`), and `basis` (`cpu-sample-path`, `heap-sample-path`, `async-stack`, or `async-cpu-window`). `stackDistance: 1` means the closest user frame to an external callee; `stackDistance: 0` means the sampled user frame itself is the fix location. Attributed findings may also expose `evidence.extra.candidateCallers[]`, ordered by proximity first and support second. Treat low-confidence attribution as an inspection lead, not automatically as the line to patch.

## `profiles.cpu`

Expand All @@ -126,7 +126,7 @@ Detail: [kinds/cpu.md](./kinds/cpu.md).
| --- | --- |
| `summary` | Total sampled bytes, top allocator, RSS / heapUsed / external / arrayBuffers stats (start/end/min/max/mean/p95) plus linear `slopeBytesPerSec`. |
| `quality` | Memory confidence gate — `confidence`, `reasons[]`, `recommendations[]`. |
| `hotAllocators` | Frames ranked by `selfBytes` / `totalBytes`, with file/line, frame category, and optional `userCaller` for external allocators. |
| `hotAllocators` | Frames ranked by `selfBytes` / `totalBytes`, with file/line, frame category, and optional `userCaller`. |
| `memoryUsage` | Compact `process.memoryUsage()` metadata (`sampleCount`, first/last sample). Raw samples present only with `--include-memory-samples`. |
| `heapSnapshotAnalysis` | Optional start/end retained-growth summary when `--heap-snapshot-analysis` is enabled. Very large snapshots return `available: false` with a warning instead of being parsed unbounded. |

Expand Down Expand Up @@ -160,6 +160,15 @@ Each finding has the same shape regardless of which kind produced it:

Findings are sorted by `priority.score`, then severity, then attributed weight.

Common `evidence.extra` anchors:

| Field | Meaning |
| --- | --- |
| `userCaller` | User-code caller or self frame that should usually be inspected before the callee/runtime frame. |
| `candidateCallers[]` | Alternative caller candidates for attributed CPU findings. |
| `correlatedAllocator` | Memory trend findings (`memory-growth:*`, `external-buffer-pressure`) use this to point from process-level growth back to an editable allocator lead. `basis` distinguishes heap-sampled allocators from CPU fallback attribution. |
| `entryFrame` | `hot-async-context:*` keeps the hot CPU frame in `evidence.*` and exposes the async chain entry point here. |

The full catalog of built-in findings, grouped by kind, is in [extending/detectors.md](./extending/detectors.md#built-in-findings).

## Schema versioning
Expand Down
Loading