@memlab/mcp-server 2.18.1 → 2.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -16
- package/bin/.eslintrc.js +26 -0
- package/bin/memlab-cli.js +313 -0
- package/dist/anonymized-snapshot.d.ts +75 -0
- package/dist/anonymized-snapshot.d.ts.map +1 -0
- package/dist/anonymized-snapshot.js +101 -0
- package/dist/anonymized-snapshot.js.map +1 -0
- package/dist/artifact-classes.d.ts +86 -0
- package/dist/artifact-classes.d.ts.map +1 -0
- package/dist/artifact-classes.js +261 -0
- package/dist/artifact-classes.js.map +1 -0
- package/dist/capture-mode.d.ts +60 -0
- package/dist/capture-mode.d.ts.map +1 -0
- package/dist/capture-mode.js +39 -0
- package/dist/capture-mode.js.map +1 -0
- package/dist/eval-hints.d.ts +41 -0
- package/dist/eval-hints.d.ts.map +1 -0
- package/dist/eval-hints.js +115 -0
- package/dist/eval-hints.js.map +1 -0
- package/dist/guardrail.d.ts.map +1 -1
- package/dist/guardrail.js +13 -0
- package/dist/guardrail.js.map +1 -1
- package/dist/heap-budget.d.ts +32 -0
- package/dist/heap-budget.d.ts.map +1 -0
- package/dist/heap-budget.js +90 -0
- package/dist/heap-budget.js.map +1 -0
- package/dist/heap-shapes.d.ts +148 -0
- package/dist/heap-shapes.d.ts.map +1 -0
- package/dist/heap-shapes.js +193 -0
- package/dist/heap-shapes.js.map +1 -0
- package/dist/heap-state.d.ts +109 -2
- package/dist/heap-state.d.ts.map +1 -1
- package/dist/heap-state.js +194 -3
- package/dist/heap-state.js.map +1 -1
- package/dist/index.js +102 -1
- package/dist/index.js.map +1 -1
- package/dist/progress.d.ts +25 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +46 -0
- package/dist/progress.js.map +1 -0
- package/dist/react-shapes.d.ts +42 -0
- package/dist/react-shapes.d.ts.map +1 -0
- package/dist/react-shapes.js +149 -0
- package/dist/react-shapes.js.map +1 -0
- package/dist/result-handles.d.ts +11 -0
- package/dist/result-handles.d.ts.map +1 -0
- package/dist/result-handles.js +77 -0
- package/dist/result-handles.js.map +1 -0
- package/dist/server-version.d.ts +19 -0
- package/dist/server-version.d.ts.map +1 -0
- package/dist/server-version.js +19 -0
- package/dist/server-version.js.map +1 -0
- package/dist/snapshot-borrow.d.ts +67 -0
- package/dist/snapshot-borrow.d.ts.map +1 -0
- package/dist/snapshot-borrow.js +113 -0
- package/dist/snapshot-borrow.js.map +1 -0
- package/dist/string-index.d.ts +55 -0
- package/dist/string-index.d.ts.map +1 -0
- package/dist/string-index.js +99 -0
- package/dist/string-index.js.map +1 -0
- package/dist/tool-registry.d.ts +42 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +20 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/tools/analyze-run.d.ts +12 -0
- package/dist/tools/analyze-run.d.ts.map +1 -0
- package/dist/tools/analyze-run.js +404 -0
- package/dist/tools/analyze-run.js.map +1 -0
- package/dist/tools/anonymize-snapshot.d.ts +12 -0
- package/dist/tools/anonymize-snapshot.d.ts.map +1 -0
- package/dist/tools/anonymize-snapshot.js +159 -0
- package/dist/tools/anonymize-snapshot.js.map +1 -0
- package/dist/tools/app-config.d.ts +12 -0
- package/dist/tools/app-config.d.ts.map +1 -0
- package/dist/tools/app-config.js +217 -0
- package/dist/tools/app-config.js.map +1 -0
- package/dist/tools/app-heap.d.ts +30 -0
- package/dist/tools/app-heap.d.ts.map +1 -0
- package/dist/tools/app-heap.js +106 -0
- package/dist/tools/app-heap.js.map +1 -0
- package/dist/tools/artifact-budget.d.ts +12 -0
- package/dist/tools/artifact-budget.d.ts.map +1 -0
- package/dist/tools/artifact-budget.js +219 -0
- package/dist/tools/artifact-budget.js.map +1 -0
- package/dist/tools/async-census.d.ts +12 -0
- package/dist/tools/async-census.d.ts.map +1 -0
- package/dist/tools/async-census.js +243 -0
- package/dist/tools/async-census.js.map +1 -0
- package/dist/tools/auto-investigate.d.ts.map +1 -1
- package/dist/tools/auto-investigate.js +54 -5
- package/dist/tools/auto-investigate.js.map +1 -1
- package/dist/tools/batch.d.ts +12 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +196 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/cache-analysis.d.ts +15 -0
- package/dist/tools/cache-analysis.d.ts.map +1 -1
- package/dist/tools/cache-analysis.js +240 -14
- package/dist/tools/cache-analysis.js.map +1 -1
- package/dist/tools/census-diff.d.ts +55 -0
- package/dist/tools/census-diff.d.ts.map +1 -0
- package/dist/tools/census-diff.js +228 -0
- package/dist/tools/census-diff.js.map +1 -0
- package/dist/tools/chain-walk.d.ts +12 -0
- package/dist/tools/chain-walk.d.ts.map +1 -0
- package/dist/tools/chain-walk.js +184 -0
- package/dist/tools/chain-walk.js.map +1 -0
- package/dist/tools/class-histogram.d.ts.map +1 -1
- package/dist/tools/class-histogram.js +14 -3
- package/dist/tools/class-histogram.js.map +1 -1
- package/dist/tools/collection-diff.d.ts +44 -0
- package/dist/tools/collection-diff.d.ts.map +1 -0
- package/dist/tools/collection-diff.js +343 -0
- package/dist/tools/collection-diff.js.map +1 -0
- package/dist/tools/collection-trend.d.ts +55 -0
- package/dist/tools/collection-trend.d.ts.map +1 -0
- package/dist/tools/collection-trend.js +315 -0
- package/dist/tools/collection-trend.js.map +1 -0
- package/dist/tools/detached-dom.d.ts +5 -0
- package/dist/tools/detached-dom.d.ts.map +1 -1
- package/dist/tools/detached-dom.js +368 -22
- package/dist/tools/detached-dom.js.map +1 -1
- package/dist/tools/dev-artifacts.d.ts +62 -6
- package/dist/tools/dev-artifacts.d.ts.map +1 -1
- package/dist/tools/dev-artifacts.js +685 -24
- package/dist/tools/dev-artifacts.js.map +1 -1
- package/dist/tools/diff-snapshots.d.ts.map +1 -1
- package/dist/tools/diff-snapshots.js +18 -2
- package/dist/tools/diff-snapshots.js.map +1 -1
- package/dist/tools/dom-audit.d.ts +37 -0
- package/dist/tools/dom-audit.d.ts.map +1 -0
- package/dist/tools/dom-audit.js +247 -0
- package/dist/tools/dom-audit.js.map +1 -0
- package/dist/tools/dominator-attribution.d.ts +12 -0
- package/dist/tools/dominator-attribution.d.ts.map +1 -0
- package/dist/tools/dominator-attribution.js +189 -0
- package/dist/tools/dominator-attribution.js.map +1 -0
- package/dist/tools/dominator-chain.d.ts +12 -0
- package/dist/tools/dominator-chain.d.ts.map +1 -0
- package/dist/tools/dominator-chain.js +92 -0
- package/dist/tools/dominator-chain.js.map +1 -0
- package/dist/tools/duplicate-objects.d.ts +12 -0
- package/dist/tools/duplicate-objects.d.ts.map +1 -0
- package/dist/tools/duplicate-objects.js +172 -0
- package/dist/tools/duplicate-objects.js.map +1 -0
- package/dist/tools/duplicated-strings.d.ts.map +1 -1
- package/dist/tools/duplicated-strings.js +49 -40
- package/dist/tools/duplicated-strings.js.map +1 -1
- package/dist/tools/eval-across.d.ts +12 -0
- package/dist/tools/eval-across.d.ts.map +1 -0
- package/dist/tools/eval-across.js +135 -0
- package/dist/tools/eval-across.js.map +1 -0
- package/dist/tools/eval.d.ts +79 -0
- package/dist/tools/eval.d.ts.map +1 -1
- package/dist/tools/eval.js +2523 -197
- package/dist/tools/eval.js.map +1 -1
- package/dist/tools/event-listener-leaks.d.ts.map +1 -1
- package/dist/tools/event-listener-leaks.js +42 -17
- package/dist/tools/event-listener-leaks.js.map +1 -1
- package/dist/tools/event-registry.d.ts.map +1 -1
- package/dist/tools/event-registry.js +42 -8
- package/dist/tools/event-registry.js.map +1 -1
- package/dist/tools/explain-delta.d.ts +12 -0
- package/dist/tools/explain-delta.d.ts.map +1 -0
- package/dist/tools/explain-delta.js +221 -0
- package/dist/tools/explain-delta.js.map +1 -0
- package/dist/tools/find-nodes-by-class.d.ts.map +1 -1
- package/dist/tools/find-nodes-by-class.js +112 -13
- package/dist/tools/find-nodes-by-class.js.map +1 -1
- package/dist/tools/finding-index.d.ts +203 -0
- package/dist/tools/finding-index.d.ts.map +1 -0
- package/dist/tools/finding-index.js +566 -0
- package/dist/tools/finding-index.js.map +1 -0
- package/dist/tools/for-each.d.ts.map +1 -1
- package/dist/tools/for-each.js +15 -3
- package/dist/tools/for-each.js.map +1 -1
- package/dist/tools/get-referrers.d.ts.map +1 -1
- package/dist/tools/get-referrers.js +17 -0
- package/dist/tools/get-referrers.js.map +1 -1
- package/dist/tools/get-value.d.ts +16 -0
- package/dist/tools/get-value.d.ts.map +1 -1
- package/dist/tools/get-value.js +96 -2
- package/dist/tools/get-value.js.map +1 -1
- package/dist/tools/hunt-report.d.ts +12 -0
- package/dist/tools/hunt-report.d.ts.map +1 -0
- package/dist/tools/hunt-report.js +186 -0
- package/dist/tools/hunt-report.js.map +1 -0
- package/dist/tools/hypothesis.d.ts +12 -0
- package/dist/tools/hypothesis.d.ts.map +1 -0
- package/dist/tools/hypothesis.js +280 -0
- package/dist/tools/hypothesis.js.map +1 -0
- package/dist/tools/id-space-audit.d.ts +12 -0
- package/dist/tools/id-space-audit.d.ts.map +1 -0
- package/dist/tools/id-space-audit.js +262 -0
- package/dist/tools/id-space-audit.js.map +1 -0
- package/dist/tools/identify.d.ts +12 -0
- package/dist/tools/identify.d.ts.map +1 -0
- package/dist/tools/identify.js +205 -0
- package/dist/tools/identify.js.map +1 -0
- package/dist/tools/identity-diff.d.ts +12 -0
- package/dist/tools/identity-diff.d.ts.map +1 -0
- package/dist/tools/identity-diff.js +188 -0
- package/dist/tools/identity-diff.js.map +1 -0
- package/dist/tools/intern-opportunities.d.ts.map +1 -1
- package/dist/tools/intern-opportunities.js +116 -50
- package/dist/tools/intern-opportunities.js.map +1 -1
- package/dist/tools/ladder-probe.d.ts +65 -0
- package/dist/tools/ladder-probe.d.ts.map +1 -0
- package/dist/tools/ladder-probe.js +445 -0
- package/dist/tools/ladder-probe.js.map +1 -0
- package/dist/tools/ladder.d.ts +33 -0
- package/dist/tools/ladder.d.ts.map +1 -0
- package/dist/tools/ladder.js +266 -0
- package/dist/tools/ladder.js.map +1 -0
- package/dist/tools/largest-objects.d.ts.map +1 -1
- package/dist/tools/largest-objects.js +6 -2
- package/dist/tools/largest-objects.js.map +1 -1
- package/dist/tools/leak-report.d.ts +35 -0
- package/dist/tools/leak-report.d.ts.map +1 -0
- package/dist/tools/leak-report.js +398 -0
- package/dist/tools/leak-report.js.map +1 -0
- package/dist/tools/load-snapshot.d.ts +29 -0
- package/dist/tools/load-snapshot.d.ts.map +1 -1
- package/dist/tools/load-snapshot.js +294 -23
- package/dist/tools/load-snapshot.js.map +1 -1
- package/dist/tools/map-entries.d.ts +12 -0
- package/dist/tools/map-entries.d.ts.map +1 -0
- package/dist/tools/map-entries.js +139 -0
- package/dist/tools/map-entries.js.map +1 -0
- package/dist/tools/match-object.d.ts +12 -0
- package/dist/tools/match-object.d.ts.map +1 -0
- package/dist/tools/match-object.js +165 -0
- package/dist/tools/match-object.js.map +1 -0
- package/dist/tools/metric.d.ts +13 -0
- package/dist/tools/metric.d.ts.map +1 -0
- package/dist/tools/metric.js +185 -0
- package/dist/tools/metric.js.map +1 -0
- package/dist/tools/module-attribution.d.ts +36 -0
- package/dist/tools/module-attribution.d.ts.map +1 -0
- package/dist/tools/module-attribution.js +235 -0
- package/dist/tools/module-attribution.js.map +1 -0
- package/dist/tools/next-measurement.d.ts +26 -0
- package/dist/tools/next-measurement.d.ts.map +1 -0
- package/dist/tools/next-measurement.js +149 -0
- package/dist/tools/next-measurement.js.map +1 -0
- package/dist/tools/object-cost-breakdown.d.ts.map +1 -1
- package/dist/tools/object-cost-breakdown.js +64 -0
- package/dist/tools/object-cost-breakdown.js.map +1 -1
- package/dist/tools/object-shape.d.ts.map +1 -1
- package/dist/tools/object-shape.js +53 -5
- package/dist/tools/object-shape.js.map +1 -1
- package/dist/tools/population-diff.d.ts +57 -0
- package/dist/tools/population-diff.d.ts.map +1 -0
- package/dist/tools/population-diff.js +338 -0
- package/dist/tools/population-diff.js.map +1 -0
- package/dist/tools/population-vs-owners.d.ts +12 -0
- package/dist/tools/population-vs-owners.d.ts.map +1 -0
- package/dist/tools/population-vs-owners.js +183 -0
- package/dist/tools/population-vs-owners.js.map +1 -0
- package/dist/tools/property-names.d.ts +29 -0
- package/dist/tools/property-names.d.ts.map +1 -0
- package/dist/tools/property-names.js +131 -0
- package/dist/tools/property-names.js.map +1 -0
- package/dist/tools/quick-diagnosis.d.ts.map +1 -1
- package/dist/tools/quick-diagnosis.js +7 -1
- package/dist/tools/quick-diagnosis.js.map +1 -1
- package/dist/tools/react-owners.d.ts +12 -0
- package/dist/tools/react-owners.d.ts.map +1 -0
- package/dist/tools/react-owners.js +167 -0
- package/dist/tools/react-owners.js.map +1 -0
- package/dist/tools/react-update-queues.d.ts +12 -0
- package/dist/tools/react-update-queues.d.ts.map +1 -0
- package/dist/tools/react-update-queues.js +173 -0
- package/dist/tools/react-update-queues.js.map +1 -0
- package/dist/tools/replicate.d.ts +49 -0
- package/dist/tools/replicate.d.ts.map +1 -0
- package/dist/tools/replicate.js +241 -0
- package/dist/tools/replicate.js.map +1 -0
- package/dist/tools/retainer-diff.d.ts +12 -0
- package/dist/tools/retainer-diff.d.ts.map +1 -0
- package/dist/tools/retainer-diff.js +168 -0
- package/dist/tools/retainer-diff.js.map +1 -0
- package/dist/tools/retainer-layers.d.ts +23 -0
- package/dist/tools/retainer-layers.d.ts.map +1 -0
- package/dist/tools/retainer-layers.js +179 -0
- package/dist/tools/retainer-layers.js.map +1 -0
- package/dist/tools/retainer-summary.d.ts +18 -0
- package/dist/tools/retainer-summary.d.ts.map +1 -1
- package/dist/tools/retainer-summary.js +210 -51
- package/dist/tools/retainer-summary.js.map +1 -1
- package/dist/tools/retainer-trace.d.ts.map +1 -1
- package/dist/tools/retainer-trace.js +14 -2
- package/dist/tools/retainer-trace.js.map +1 -1
- package/dist/tools/round-audit.d.ts +66 -0
- package/dist/tools/round-audit.d.ts.map +1 -0
- package/dist/tools/round-audit.js +258 -0
- package/dist/tools/round-audit.js.map +1 -0
- package/dist/tools/round-digest.d.ts +12 -0
- package/dist/tools/round-digest.d.ts.map +1 -0
- package/dist/tools/round-digest.js +190 -0
- package/dist/tools/round-digest.js.map +1 -0
- package/dist/tools/script-census.d.ts +12 -0
- package/dist/tools/script-census.d.ts.map +1 -0
- package/dist/tools/script-census.js +182 -0
- package/dist/tools/script-census.js.map +1 -0
- package/dist/tools/search-nodes.d.ts.map +1 -1
- package/dist/tools/search-nodes.js +5 -1
- package/dist/tools/search-nodes.js.map +1 -1
- package/dist/tools/search-strings.d.ts.map +1 -1
- package/dist/tools/search-strings.js +53 -7
- package/dist/tools/search-strings.js.map +1 -1
- package/dist/tools/sequence-analysis.d.ts +53 -0
- package/dist/tools/sequence-analysis.d.ts.map +1 -1
- package/dist/tools/sequence-analysis.js +313 -89
- package/dist/tools/sequence-analysis.js.map +1 -1
- package/dist/tools/server-status.d.ts.map +1 -1
- package/dist/tools/server-status.js +4 -1
- package/dist/tools/server-status.js.map +1 -1
- package/dist/tools/settle-check.d.ts +12 -0
- package/dist/tools/settle-check.d.ts.map +1 -0
- package/dist/tools/settle-check.js +188 -0
- package/dist/tools/settle-check.js.map +1 -0
- package/dist/tools/shape-histogram.d.ts.map +1 -1
- package/dist/tools/shape-histogram.js +9 -2
- package/dist/tools/shape-histogram.js.map +1 -1
- package/dist/tools/sliced-strings.d.ts.map +1 -1
- package/dist/tools/sliced-strings.js +2 -1
- package/dist/tools/sliced-strings.js.map +1 -1
- package/dist/tools/snapshots.d.ts.map +1 -1
- package/dist/tools/snapshots.js +24 -6
- package/dist/tools/snapshots.js.map +1 -1
- package/dist/tools/sparse-elements.d.ts +12 -0
- package/dist/tools/sparse-elements.d.ts.map +1 -0
- package/dist/tools/sparse-elements.js +215 -0
- package/dist/tools/sparse-elements.js.map +1 -0
- package/dist/tools/stale-collections.d.ts +17 -0
- package/dist/tools/stale-collections.d.ts.map +1 -1
- package/dist/tools/stale-collections.js +28 -0
- package/dist/tools/stale-collections.js.map +1 -1
- package/dist/tools/string-patterns.d.ts.map +1 -1
- package/dist/tools/string-patterns.js +7 -5
- package/dist/tools/string-patterns.js.map +1 -1
- package/dist/tools/tools-index.d.ts +12 -0
- package/dist/tools/tools-index.d.ts.map +1 -0
- package/dist/tools/tools-index.js +492 -0
- package/dist/tools/tools-index.js.map +1 -0
- package/dist/tools/trace-all.d.ts +42 -0
- package/dist/tools/trace-all.d.ts.map +1 -0
- package/dist/tools/trace-all.js +263 -0
- package/dist/tools/trace-all.js.map +1 -0
- package/dist/tools/unit-cost.d.ts +12 -0
- package/dist/tools/unit-cost.d.ts.map +1 -0
- package/dist/tools/unit-cost.js +219 -0
- package/dist/tools/unit-cost.js.map +1 -0
- package/dist/tools/verify-fix.d.ts +23 -0
- package/dist/tools/verify-fix.d.ts.map +1 -0
- package/dist/tools/verify-fix.js +219 -0
- package/dist/tools/verify-fix.js.map +1 -0
- package/dist/tools/weakmap-entries.d.ts.map +1 -1
- package/dist/tools/weakmap-entries.js +6 -1
- package/dist/tools/weakmap-entries.js.map +1 -1
- package/dist/tools/weakref-census.d.ts +15 -0
- package/dist/tools/weakref-census.d.ts.map +1 -0
- package/dist/tools/weakref-census.js +147 -0
- package/dist/tools/weakref-census.js.map +1 -0
- package/dist/tools/what-if.d.ts +12 -0
- package/dist/tools/what-if.d.ts.map +1 -0
- package/dist/tools/what-if.js +285 -0
- package/dist/tools/what-if.js.map +1 -0
- package/dist/utils.d.ts +238 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +507 -15
- package/dist/utils.js.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall memory_lab
|
|
9
|
+
*/
|
|
10
|
+
import memlabCore from '@memlab/core';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { getSnapshot, getSnapshotMetadata } from '../heap-state.js';
|
|
13
|
+
import { boundedDominatorRetainedSize, errorResult, formatBytes, formatNumber, markdownTable, toolResult, } from '../utils.js';
|
|
14
|
+
const { NumericSet } = memlabCore;
|
|
15
|
+
function matchesShape(node, shape) {
|
|
16
|
+
const want = new Set(shape);
|
|
17
|
+
for (const e of node.references) {
|
|
18
|
+
if (e.type !== 'property')
|
|
19
|
+
continue;
|
|
20
|
+
want.delete(String(e.name_or_index));
|
|
21
|
+
if (want.size === 0)
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Second-order collectability: what ELSE dies when this population goes away.
|
|
28
|
+
*
|
|
29
|
+
* The dominator-deduped figure is a floor by construction — it counts only what
|
|
30
|
+
* the population exclusively dominates. An object reachable by two paths, both
|
|
31
|
+
* of which run through the population, is dominated by neither member and so is
|
|
32
|
+
* counted by neither, yet it becomes garbage the moment they do. On a graph with
|
|
33
|
+
* shared substructure that is not a rounding error.
|
|
34
|
+
*
|
|
35
|
+
* Answering it properly means recomputing reachability, which is what this does:
|
|
36
|
+
* a BFS from the GC roots with the population's edges removed, compared against
|
|
37
|
+
* the same BFS with them present. It is O(N+E) twice rather than a dominator
|
|
38
|
+
* pass, so it is affordable, but it is a real graph walk and not a heuristic.
|
|
39
|
+
*/
|
|
40
|
+
function findRootIds(snapshot) {
|
|
41
|
+
const roots = [];
|
|
42
|
+
snapshot.nodes.forEach(node => {
|
|
43
|
+
if (roots.length > 0)
|
|
44
|
+
return;
|
|
45
|
+
if (node.type === 'synthetic' && node.name.includes('GC roots')) {
|
|
46
|
+
roots.push(node.id);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (roots.length === 0) {
|
|
50
|
+
// Fall back to every node V8 marks as unreachable-from-nothing: the first
|
|
51
|
+
// node in a V8 snapshot is the root by convention.
|
|
52
|
+
snapshot.nodes.forEach(node => {
|
|
53
|
+
if (roots.length === 0)
|
|
54
|
+
roots.push(node.id);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return roots;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Reachability marking uses a flat byte array indexed by node id, not a Set.
|
|
61
|
+
* A Set of ids throws "Set maximum size exceeded" on a large server heap
|
|
62
|
+
* (observed on a 9.9M-node Node.js capture), and the failure lands in the
|
|
63
|
+
* middle of an otherwise-complete analysis. The array costs one byte per id
|
|
64
|
+
* slot — tens of MB on the largest heaps that load at all — and cannot hit a
|
|
65
|
+
* collection limit.
|
|
66
|
+
*/
|
|
67
|
+
function maxNodeId(snapshot) {
|
|
68
|
+
let max = 0;
|
|
69
|
+
snapshot.nodes.forEach(node => {
|
|
70
|
+
if (node.id > max)
|
|
71
|
+
max = node.id;
|
|
72
|
+
});
|
|
73
|
+
return max;
|
|
74
|
+
}
|
|
75
|
+
function reachableFrom(snapshot, rootIds, removed, maxId) {
|
|
76
|
+
const seen = new Uint8Array(maxId + 1);
|
|
77
|
+
const stack = [];
|
|
78
|
+
for (const id of rootIds) {
|
|
79
|
+
if (id > maxId || removed[id] === 1)
|
|
80
|
+
continue;
|
|
81
|
+
seen[id] = 1;
|
|
82
|
+
stack.push(id);
|
|
83
|
+
}
|
|
84
|
+
while (stack.length > 0) {
|
|
85
|
+
const id = stack.pop();
|
|
86
|
+
const node = snapshot.getNodeById(id);
|
|
87
|
+
if (node == null)
|
|
88
|
+
continue;
|
|
89
|
+
for (const edge of node.references) {
|
|
90
|
+
// A weak edge does not keep its target alive, so following it would
|
|
91
|
+
// report objects as surviving that a real GC would collect.
|
|
92
|
+
if (edge.type === 'weak')
|
|
93
|
+
continue;
|
|
94
|
+
const toId = edge.toNode.id;
|
|
95
|
+
if (toId > maxId || removed[toId] === 1 || seen[toId] === 1)
|
|
96
|
+
continue;
|
|
97
|
+
seen[toId] = 1;
|
|
98
|
+
stack.push(toId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return seen;
|
|
102
|
+
}
|
|
103
|
+
export function registerWhatIf(server) {
|
|
104
|
+
server.tool('memlab_what_if', 'If this set of objects were freed, how many bytes would actually come back? Reports the dominator-deduped retained size of a population — the bytes that go away when it does, with nothing double-counted and nothing counted that is also reachable another way.\n\n' +
|
|
105
|
+
'This is the number every fix decision needs and the one that is easiest to get wrong by hand. Summing per-object retained sizes double-counts every nested object; quoting the class total counts memory that other live code still holds; quoting a container\'s retained size includes internals that survive. `memlab_detached_dom` already reports it for detached subtrees as "Owner Frees" — this makes the same question askable about any set.\n\n' +
|
|
106
|
+
'Specify the population by ids, class name, or property shape.', {
|
|
107
|
+
node_ids: z
|
|
108
|
+
.array(z.number())
|
|
109
|
+
.optional()
|
|
110
|
+
.describe('Explicit node ids to free (from any tool that reports ids).'),
|
|
111
|
+
class_name: z
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe('Free every instance of this class (exact name match).'),
|
|
115
|
+
shape: z
|
|
116
|
+
.array(z.string())
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Free every object carrying ALL of these properties — the usable selector on a minified heap.'),
|
|
119
|
+
breakdown: z
|
|
120
|
+
.boolean()
|
|
121
|
+
.optional()
|
|
122
|
+
.default(true)
|
|
123
|
+
.describe('Also report what the freed bytes consist of, grouped by the class of the dominated nodes (default true). Answers "am I freeing the records, or the data they point at?".'),
|
|
124
|
+
limit: z
|
|
125
|
+
.number()
|
|
126
|
+
.optional()
|
|
127
|
+
.default(12)
|
|
128
|
+
.describe('Rows in the breakdown table (default 12).'),
|
|
129
|
+
cascade: z
|
|
130
|
+
.boolean()
|
|
131
|
+
.optional()
|
|
132
|
+
.default(false)
|
|
133
|
+
.describe('Also compute the SECOND-ORDER effect: objects that become unreachable because the population was their last live referrer, even though no member dominated them. Recomputes reachability from the GC roots with the population removed (two O(N+E) walks — seconds on a multi-million-node heap, not instant). Off by default because the dominator figure alone is the conservative answer.'),
|
|
134
|
+
}, async ({ node_ids, class_name, shape, breakdown, limit, cascade }) => {
|
|
135
|
+
try {
|
|
136
|
+
const snapshot = getSnapshot();
|
|
137
|
+
const selectors = [
|
|
138
|
+
node_ids != null && node_ids.length > 0,
|
|
139
|
+
class_name != null,
|
|
140
|
+
shape != null && shape.length > 0,
|
|
141
|
+
].filter(Boolean).length;
|
|
142
|
+
if (selectors === 0) {
|
|
143
|
+
return errorResult(new Error('Pass node_ids, class_name or shape to select what to free.'));
|
|
144
|
+
}
|
|
145
|
+
if (selectors > 1) {
|
|
146
|
+
return errorResult(new Error('Pass exactly one of node_ids / class_name / shape — combining them would make the reported population ambiguous.'));
|
|
147
|
+
}
|
|
148
|
+
const ids = [];
|
|
149
|
+
if (node_ids != null && node_ids.length > 0) {
|
|
150
|
+
for (const id of node_ids) {
|
|
151
|
+
if (snapshot.getNodeById(id) != null)
|
|
152
|
+
ids.push(id);
|
|
153
|
+
}
|
|
154
|
+
if (ids.length === 0) {
|
|
155
|
+
return errorResult(new Error('None of those ids exist in the active snapshot. Node ids are per-capture — check the handle with memlab_snapshots.'));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
snapshot.nodes.forEach(node => {
|
|
160
|
+
if (node.id <= 3)
|
|
161
|
+
return;
|
|
162
|
+
if (class_name != null) {
|
|
163
|
+
if (node.name !== class_name)
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
else if (shape != null && !matchesShape(node, shape)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
ids.push(node.id);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (ids.length === 0) {
|
|
173
|
+
return toolResult(`Nothing matched, so there is nothing to free. Check the class name with \`memlab_class_histogram\` or the shape with \`memlab_shape_histogram\`.`);
|
|
174
|
+
}
|
|
175
|
+
const idSet = new NumericSet(ids);
|
|
176
|
+
const { retained, exact } = boundedDominatorRetainedSize(idSet, snapshot);
|
|
177
|
+
const meta = getSnapshotMetadata();
|
|
178
|
+
const heapTotal = meta?.totalSize ?? 0;
|
|
179
|
+
const share = heapTotal > 0 ? (retained / heapTotal) * 100 : 0;
|
|
180
|
+
let selfTotal = 0;
|
|
181
|
+
for (const id of ids) {
|
|
182
|
+
selfTotal += snapshot.getNodeById(id)?.self_size ?? 0;
|
|
183
|
+
}
|
|
184
|
+
const label = class_name != null
|
|
185
|
+
? `class \`${class_name}\``
|
|
186
|
+
: shape != null
|
|
187
|
+
? `shape \`{${shape.join(', ')}}\``
|
|
188
|
+
: `${formatNumber(ids.length)} explicit id(s)`;
|
|
189
|
+
const lines = [
|
|
190
|
+
`## What if ${label} were freed?`,
|
|
191
|
+
'',
|
|
192
|
+
`**${formatBytes(retained)} would be reclaimed**${exact ? '' : ' (upper bound — the dominator walk hit its depth cap)'}` +
|
|
193
|
+
`${heapTotal > 0 ? `, ${share.toFixed(1)}% of the ${formatBytes(heapTotal)} heap` : ''}.`,
|
|
194
|
+
'',
|
|
195
|
+
`- Population: ${formatNumber(ids.length)} object(s)`,
|
|
196
|
+
`- Their own bytes (self size): ${formatBytes(selfTotal)}`,
|
|
197
|
+
`- Everything they exclusively dominate: ${formatBytes(retained)}`,
|
|
198
|
+
'',
|
|
199
|
+
retained > selfTotal * 2
|
|
200
|
+
? `Most of the win is in what these objects HOLD, not in the objects themselves (${formatBytes(retained - selfTotal)} of the total). Freeing them is worth it only if nothing else references that payload — which is exactly what dominator-deduped means, so this figure already accounts for it.`
|
|
201
|
+
: "Nearly all of the win is the objects' own bytes; they hold little exclusively.",
|
|
202
|
+
];
|
|
203
|
+
if (breakdown) {
|
|
204
|
+
// What the reclaimed bytes actually consist of. "Freeing 26 MB" reads
|
|
205
|
+
// very differently once you know 90% of it is one payload class.
|
|
206
|
+
const byClass = new Map();
|
|
207
|
+
const seen = new Set();
|
|
208
|
+
for (const id of ids) {
|
|
209
|
+
const start = snapshot.getNodeById(id);
|
|
210
|
+
if (start == null)
|
|
211
|
+
continue;
|
|
212
|
+
const stack = [start];
|
|
213
|
+
while (stack.length > 0) {
|
|
214
|
+
const n = stack.pop();
|
|
215
|
+
if (seen.has(n.id))
|
|
216
|
+
continue;
|
|
217
|
+
seen.add(n.id);
|
|
218
|
+
const key = `${n.name} (${n.type})`;
|
|
219
|
+
const e = byClass.get(key) ?? { count: 0, bytes: 0 };
|
|
220
|
+
e.count++;
|
|
221
|
+
e.bytes += n.self_size;
|
|
222
|
+
byClass.set(key, e);
|
|
223
|
+
for (const edge of n.references) {
|
|
224
|
+
const child = edge.toNode;
|
|
225
|
+
if (child.id > 3 &&
|
|
226
|
+
!seen.has(child.id) &&
|
|
227
|
+
child.dominatorNode?.id === n.id) {
|
|
228
|
+
stack.push(child);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const rows = [...byClass.entries()]
|
|
234
|
+
.sort((a, b) => b[1].bytes - a[1].bytes)
|
|
235
|
+
.slice(0, limit)
|
|
236
|
+
.map(([name, e]) => [
|
|
237
|
+
name.length > 50 ? name.slice(0, 47) + '…' : name,
|
|
238
|
+
formatNumber(e.count),
|
|
239
|
+
formatBytes(e.bytes),
|
|
240
|
+
]);
|
|
241
|
+
lines.push('', '### What the reclaimed bytes are', '', markdownTable(['Class', 'Nodes', 'Self size'], rows, new Set([1, 2])));
|
|
242
|
+
}
|
|
243
|
+
if (cascade) {
|
|
244
|
+
const maxId = maxNodeId(snapshot);
|
|
245
|
+
const removed = new Uint8Array(maxId + 1);
|
|
246
|
+
for (const id of ids)
|
|
247
|
+
if (id <= maxId)
|
|
248
|
+
removed[id] = 1;
|
|
249
|
+
const none = new Uint8Array(maxId + 1);
|
|
250
|
+
const roots = findRootIds(snapshot);
|
|
251
|
+
const before = reachableFrom(snapshot, roots, none, maxId);
|
|
252
|
+
const after = reachableFrom(snapshot, roots, removed, maxId);
|
|
253
|
+
let cascadeBytes = 0;
|
|
254
|
+
let cascadeNodes = 0;
|
|
255
|
+
for (let id = 0; id <= maxId; id++) {
|
|
256
|
+
if (before[id] !== 1)
|
|
257
|
+
continue;
|
|
258
|
+
// The population's own bytes are already counted; this walk is
|
|
259
|
+
// about what ELSE dies with it.
|
|
260
|
+
if (after[id] === 1 || removed[id] === 1)
|
|
261
|
+
continue;
|
|
262
|
+
cascadeNodes++;
|
|
263
|
+
cascadeBytes += snapshot.getNodeById(id)?.self_size ?? 0;
|
|
264
|
+
}
|
|
265
|
+
// The dominator figure splits into the population's own bytes plus
|
|
266
|
+
// what it exclusively dominates. Only the second part is comparable
|
|
267
|
+
// to the cascade walk, which excludes the population itself.
|
|
268
|
+
const dominatedNonSelf = Math.max(0, retained - selfTotal);
|
|
269
|
+
const extra = cascadeBytes - dominatedNonSelf;
|
|
270
|
+
const totalFreed = selfTotal + cascadeBytes;
|
|
271
|
+
lines.push('', '### Second-order effect (cascade)', '', `Recomputing reachability from ${formatNumber(roots.length)} GC root(s) with this population removed: **${formatNumber(cascadeNodes)} node(s) outside the population / ${formatBytes(cascadeBytes)} become unreachable**, against ${formatBytes(dominatedNonSelf)} that the population exclusively dominates.`, '', extra > 0
|
|
272
|
+
? `**${formatBytes(extra)} of that is second-order** — objects no member dominated (they were reachable by several paths, all of which ran through the population) but which nothing else keeps alive. Total freed including the population's own ${formatBytes(selfTotal)}: **${formatBytes(totalFreed)}**, against the ${formatBytes(retained)} dominator figure.`
|
|
273
|
+
: extra === 0
|
|
274
|
+
? 'No second-order effect: the exclusively-dominated set is exactly what becomes unreachable.'
|
|
275
|
+
: `The cascade walk found ${formatBytes(-extra)} LESS than the dominator figure claims is exclusively dominated. That is a discrepancy, not a saving — it means part of the dominated set stays reachable in this walk, most likely through an edge type the walk skips (weak) or a root the walk did not start from (${formatNumber(roots.length)} found). Trust the dominator figure and treat this cascade run as inconclusive.`, '', '_Reachability only. It does not model finalizers, and it treats weak edges as non-retaining (which is correct for collectability but means a WeakMap value shown as freed is freed only if its key also dies)._');
|
|
276
|
+
}
|
|
277
|
+
lines.push('', '_Counts only what this population EXCLUSIVELY dominates: anything also reachable from elsewhere stays and is not included. Second-order effects — objects that become collectable because a freed one was their last referrer without dominating them — are not in this figure, so treat it as a floor; pass `cascade: true` to measure them._');
|
|
278
|
+
return toolResult(lines.join('\n'));
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
return errorResult(err);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=what-if.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"what-if.js","sourceRoot":"","sources":["../../src/tools/what-if.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -40,12 +40,26 @@ export interface IncomingEdge extends EdgeSummary {
|
|
|
40
40
|
from_node: NodeSummary;
|
|
41
41
|
}
|
|
42
42
|
export declare function truncateDomToTag(name: string): string;
|
|
43
|
+
export declare const BLINK_TEMPLATE_ELISION = "<\u2026>";
|
|
44
|
+
export declare function abbreviateBlinkTypeName(name: string): string;
|
|
43
45
|
export declare function truncateNodeName(name: string, type: string, selfSize: number, maxLen?: number, aggressiveDom?: boolean): string;
|
|
44
46
|
export declare function serializeNodeSummary(node: IHeapNode): NodeSummary;
|
|
45
47
|
export declare function serializeNodeDetail(node: IHeapNode): NodeDetail;
|
|
46
48
|
export declare function serializeOutgoingEdge(edge: IHeapEdge): OutgoingEdge;
|
|
47
49
|
export declare function serializeIncomingEdge(edge: IHeapEdge): IncomingEdge;
|
|
48
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Whether a node is worth showing in a default "interesting objects" listing.
|
|
52
|
+
*
|
|
53
|
+
* `NODE_TYPE_BLOCK_LIST` exists to keep noise out of the DEFAULT view — it is not
|
|
54
|
+
* meant to make those types unreachable. When a caller explicitly asks for one
|
|
55
|
+
* of them (e.g. `largest_objects({node_type: 'array'})`), honour the request:
|
|
56
|
+
* otherwise the filter silently returns zero rows for `array`, `native`, `code`,
|
|
57
|
+
* `synthetic` and `hidden`, which reads as "there are none" on a heap that
|
|
58
|
+
* plainly contains 100k+ of them.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isNodeWorthInspecting(node: IHeapNode, opts?: {
|
|
61
|
+
allowBlockedTypes?: boolean;
|
|
62
|
+
}): boolean;
|
|
49
63
|
/**
|
|
50
64
|
* Property names that mark a "freshness" timestamp on a TTL/warm cache wrapper
|
|
51
65
|
* object (e.g. `{entries, loadedAt}`, `{items, timestamp}`). Their presence next
|
|
@@ -71,6 +85,21 @@ export declare function hasFreshnessTimestampSibling(node: IHeapNode): boolean;
|
|
|
71
85
|
*/
|
|
72
86
|
export declare function looksLikeFailurePayload(value: string): boolean;
|
|
73
87
|
export declare function filterLargestObjects(snapshot: IHeapSnapshot, filter: (node: IHeapNode) => boolean, limit: number): IHeapNode[];
|
|
88
|
+
/**
|
|
89
|
+
* As `filterLargestObjects`, but also reports how many nodes MATCHED.
|
|
90
|
+
*
|
|
91
|
+
* The population size is not a nicety. A conclusion drawn from the top 5 of a
|
|
92
|
+
* matching population reads identically whether that population was 5 or 23,918,
|
|
93
|
+
* and the difference decides whether the conclusion is sound: one report stated
|
|
94
|
+
* "all 5 sampled instances share the same retainer pattern — likely a single
|
|
95
|
+
* root cause" where a full-population trace found 1,468 distinct paths, the
|
|
96
|
+
* largest covering 2.2%. The sample fraction has to reach the caller, so the
|
|
97
|
+
* walk that already visits every node returns it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function filterLargestObjectsCounted(snapshot: IHeapSnapshot, filter: (node: IHeapNode) => boolean, limit: number): {
|
|
100
|
+
nodes: IHeapNode[];
|
|
101
|
+
matched: number;
|
|
102
|
+
};
|
|
74
103
|
/**
|
|
75
104
|
* Collapse heap nodes that are merely different VIEWS of the same dominator
|
|
76
105
|
* subtree (e.g. on a single in-flight request: Client → Request → closure →
|
|
@@ -128,6 +157,85 @@ export declare function boundedDominatorRetainedSize(ids: HeapNodeIdSet, snapsho
|
|
|
128
157
|
retained: number;
|
|
129
158
|
exact: boolean;
|
|
130
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* One backing-store slot of a Map/Set/WeakMap: its true FixedArray index
|
|
162
|
+
* (`name_or_index`) and the heap node it points at.
|
|
163
|
+
*/
|
|
164
|
+
export interface BackingSlot {
|
|
165
|
+
idx: number;
|
|
166
|
+
node: IHeapNode;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* One enumerated Map/WeakMap entry. `value` is null when the value slot held an
|
|
170
|
+
* inline SMI (no heap edge) — see `enumerateMapEntries`.
|
|
171
|
+
*/
|
|
172
|
+
export interface CollectionEntry {
|
|
173
|
+
key: IHeapNode;
|
|
174
|
+
value: IHeapNode | null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Collect the object-valued slots of a Map/Set/WeakMap backing store, sorted by
|
|
178
|
+
* their true FixedArray slot index.
|
|
179
|
+
*
|
|
180
|
+
* V8 stores entries in an OrderedHashMap/OrderedHashSet reachable via the
|
|
181
|
+
* `table` (or `backing_store`) edge. Browser (Chromium) snapshots type the
|
|
182
|
+
* key/value slot edges as `internal` with a numeric name (the array index);
|
|
183
|
+
* Node snapshots type them `element`. The header counts and hash-chain links are
|
|
184
|
+
* inline SMIs, which emit NO edge at all — so the slot indices are NOT
|
|
185
|
+
* contiguous, and the GAPS are meaningful (they mark SMI-valued or deleted
|
|
186
|
+
* slots). Returning the slots WITH their indices lets callers pair by index
|
|
187
|
+
* instead of by position, which is what makes the pairing correct when some
|
|
188
|
+
* values are SMIs (see `enumerateMapEntries`).
|
|
189
|
+
*/
|
|
190
|
+
export declare function collectBackingSlots(node: IHeapNode): BackingSlot[];
|
|
191
|
+
/**
|
|
192
|
+
* Enumerate a Map/WeakMap's (key, value) entries.
|
|
193
|
+
*
|
|
194
|
+
* V8 lays each entry out as consecutive slots `[key, value, chain]` (the chain
|
|
195
|
+
* link is an inline SMI, so it emits no edge). We therefore pair a key with the
|
|
196
|
+
* value in the immediately-following slot INDEX (`key.idx + 1`). When no slot
|
|
197
|
+
* exists at `key.idx + 1`, the value was an inline SMI/primitive and we emit
|
|
198
|
+
* `value: null` — instead of mispairing the key with the NEXT entry's key, which
|
|
199
|
+
* is what naive "consecutive object edges = key,value" pairing does whenever a
|
|
200
|
+
* Map has SMI values (a real correctness bug on browser heaps).
|
|
201
|
+
*/
|
|
202
|
+
export declare function enumerateMapEntries(node: IHeapNode): CollectionEntry[];
|
|
203
|
+
/** Enumerate a Set's elements (every occupied backing-store slot). */
|
|
204
|
+
export declare function enumerateSetElements(node: IHeapNode): IHeapNode[];
|
|
205
|
+
/**
|
|
206
|
+
* Property names that identify an individual record rather than describe its
|
|
207
|
+
* content — always unique per instance, so INCLUDING them in a content
|
|
208
|
+
* signature defeats duplicate detection (every record looks distinct). Excluded
|
|
209
|
+
* by default from `objectContentSignature`. `id`/`__id`/`__ref` are Relay/GraphQL
|
|
210
|
+
* data-id fields; `key`/`clientMutationId` are common per-instance keys.
|
|
211
|
+
*/
|
|
212
|
+
export declare const IDENTITY_PROPS: ReadonlySet<string>;
|
|
213
|
+
/**
|
|
214
|
+
* A stable, shallow content signature for an object: the sorted set of property
|
|
215
|
+
* names, each annotated with its scalar value (strings capped) or a marker for
|
|
216
|
+
* object/array-valued properties. Two objects with the same signature are
|
|
217
|
+
* structurally identical at one level — the basis for duplicate-record
|
|
218
|
+
* detection (`memlab_duplicate_objects`) and ad-hoc dedup checks in eval.
|
|
219
|
+
*
|
|
220
|
+
* By default per-instance identity fields (`IDENTITY_PROPS`: id/__id/__ref/…) are
|
|
221
|
+
* EXCLUDED so records that differ only by their data-id still collapse together
|
|
222
|
+
* — pass `ignoreProps` to override the set (e.g. `new Set()` to include them).
|
|
223
|
+
*
|
|
224
|
+
* String, boolean, null and object-valued properties are captured (objects
|
|
225
|
+
* generically as `o`, so nested content is not compared).
|
|
226
|
+
*
|
|
227
|
+
* Caveat — numeric values are NOT captured: the V8 heap-snapshot format does not
|
|
228
|
+
* store the actual value of a number field (SMI nodes are `smi number`,
|
|
229
|
+
* heap-numbers are `heap number` — the IEEE-754/int value is not accessible; see
|
|
230
|
+
* `tools/get-value.ts`). A numeric property therefore contributes only a generic
|
|
231
|
+
* `<name>=n` marker, so two objects differing ONLY in a numeric field hash to the
|
|
232
|
+
* same signature and are reported as duplicates. Treat numeric-heavy records
|
|
233
|
+
* accordingly (or compare their values via another tool).
|
|
234
|
+
*/
|
|
235
|
+
export declare function objectContentSignature(node: IHeapNode, opts?: {
|
|
236
|
+
maxStringLen?: number;
|
|
237
|
+
ignoreProps?: ReadonlySet<string>;
|
|
238
|
+
}): string;
|
|
131
239
|
/**
|
|
132
240
|
* Best-effort detection of which app a Node snapshot came from, by tallying the
|
|
133
241
|
* `/app(s)/<name>/` segment in bundle paths embedded throughout the heap (string
|
|
@@ -149,6 +257,54 @@ export declare function instrumentationRetainerNote(steps: Array<{
|
|
|
149
257
|
name: string;
|
|
150
258
|
edgeName?: string;
|
|
151
259
|
}>): string | null;
|
|
260
|
+
/**
|
|
261
|
+
* The nearest owner reachable by a NAMED property, plus HOW the target hangs
|
|
262
|
+
* off it. `Owner.prop` alone is not an address — the anchor is frequently an
|
|
263
|
+
* ancestor, and reading it as the object's own location is wrong for a large
|
|
264
|
+
* fraction of a real heap.
|
|
265
|
+
*
|
|
266
|
+
* Measured over 3,009 candidate objects on one browser capture: 1,815 hung
|
|
267
|
+
* directly off a named property, 1,084 were entries inside a Map or array, and
|
|
268
|
+
* 105 were variables captured in a closure. For the closure cases the bare
|
|
269
|
+
* anchor also DISCARDED the local variable name — `EMOJI_LIST`,
|
|
270
|
+
* `fbTop50Emojis`, `specialTags` were each reported under an unrelated
|
|
271
|
+
* sibling's property name, which is the single most greppable thing about them.
|
|
272
|
+
*
|
|
273
|
+
* Grouping by the immediate referrer edge does not work either: objects held in
|
|
274
|
+
* a Map are keyed by a numeric slot index in the backing table, so 552
|
|
275
|
+
* identical objects fragment into 552 groups of one. And in a minified bundle
|
|
276
|
+
* the class name is `Object` for all of them.
|
|
277
|
+
*/
|
|
278
|
+
export interface NamedOwner {
|
|
279
|
+
/** `<OwnerClass>.<property>` — the greppable part. */
|
|
280
|
+
anchor: string;
|
|
281
|
+
/**
|
|
282
|
+
* `null` when the anchor IS the target's own property slot; otherwise how the
|
|
283
|
+
* target sits under it (`entry`, `closure var caches`, ...).
|
|
284
|
+
*/
|
|
285
|
+
relation: string | null;
|
|
286
|
+
/** `anchor`, or `anchor ▸ relation` — what to show a reader. */
|
|
287
|
+
label: string;
|
|
288
|
+
ownerNode: IHeapNode;
|
|
289
|
+
hops: number;
|
|
290
|
+
}
|
|
291
|
+
export declare function nearestNamedOwner(node: IHeapNode, opts?: {
|
|
292
|
+
maxHops?: number;
|
|
293
|
+
}): NamedOwner | null;
|
|
294
|
+
export interface EphemeronEdge {
|
|
295
|
+
keyName: string;
|
|
296
|
+
keyId: number;
|
|
297
|
+
valueName: string;
|
|
298
|
+
valueId: number;
|
|
299
|
+
}
|
|
300
|
+
/** Parse a WeakMap ephemeron edge name, or `null` if it is an ordinary edge. */
|
|
301
|
+
export declare function parseEphemeronEdge(edgeName: string | undefined | null): EphemeronEdge | null;
|
|
302
|
+
/**
|
|
303
|
+
* The caveat to print when a path traverses an ephemeron edge. `atTarget` marks
|
|
304
|
+
* the case where it is the LAST hop, which is the one that invalidates the
|
|
305
|
+
* conclusion outright rather than merely complicating it.
|
|
306
|
+
*/
|
|
307
|
+
export declare function ephemeronCaveat(eph: EphemeronEdge, atTarget: boolean): string;
|
|
152
308
|
export declare function formatBytes(bytes: number): string;
|
|
153
309
|
export declare function formatNumber(n: number): string;
|
|
154
310
|
export declare function markdownTable(headers: string[], rows: string[][], alignRight?: Set<number>): string;
|
|
@@ -169,7 +325,39 @@ export interface RetainerTreeStep {
|
|
|
169
325
|
edgeName?: string;
|
|
170
326
|
collapsedBefore?: number;
|
|
171
327
|
collapsedNote?: string;
|
|
328
|
+
repeatCount?: number;
|
|
172
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Collapse runs of consecutive same-class hops in a retainer path into a single
|
|
332
|
+
* step tagged with `repeatCount`.
|
|
333
|
+
*
|
|
334
|
+
* Linked structures (a React hook update queue's `pending → .next → .next → …`,
|
|
335
|
+
* intrusive lists, promise chains) produce paths that are hundreds of identical
|
|
336
|
+
* `Object → Object → Object` hops long. Rendering every hop costs thousands of
|
|
337
|
+
* tokens and tells the reader nothing the count doesn't: the interesting part is
|
|
338
|
+
* the chain's head, its length, and where it attaches. This keeps the first hop
|
|
339
|
+
* of each run (so the edge into the run is preserved) and folds the rest.
|
|
340
|
+
*
|
|
341
|
+
* Only runs of at least `minRun` are collapsed, so ordinary short paths render
|
|
342
|
+
* exactly as before.
|
|
343
|
+
*/
|
|
344
|
+
export declare function collapseRepeatedRuns(steps: RetainerTreeStep[], minRun?: number): RetainerTreeStep[];
|
|
345
|
+
/**
|
|
346
|
+
* Collapse runs of an identical rendered label, for formatters that have already
|
|
347
|
+
* reduced each hop to a string. Mirrors `collapseRepeatedRuns` for chain-style
|
|
348
|
+
* (`A → B → C`) renderers.
|
|
349
|
+
*/
|
|
350
|
+
export declare function collapseRepeatedLabels(labels: string[], minRun?: number): string[];
|
|
351
|
+
/**
|
|
352
|
+
* One-line note describing what a tool's thresholds excluded from its own
|
|
353
|
+
* output. Printing this everywhere a default threshold exists prevents the
|
|
354
|
+
* failure mode where a tool reports "0" or "none found" and the reader takes it
|
|
355
|
+
* as "there is nothing there", when in fact everything interesting was below the
|
|
356
|
+
* cutoff. (A `dev_artifacts` run reporting `0 B` dev-only — because every
|
|
357
|
+
* console-retained string was under the 512 KB default — nearly landed a
|
|
358
|
+
* measurement artifact in a diff as a production leak.)
|
|
359
|
+
*/
|
|
360
|
+
export declare function describeSkipped(examined: number, skipped: number, thresholdLabel: string): string;
|
|
173
361
|
/**
|
|
174
362
|
* Render a retainer path so it reads in a single, unambiguous direction: the GC
|
|
175
363
|
* root is at the top, and **every node is retained by the one above it**. The
|
|
@@ -191,15 +379,57 @@ export declare function formatRetainerTree(steps: RetainerTreeStep[], opts?: {
|
|
|
191
379
|
maxTreeDepth?: number;
|
|
192
380
|
}): string;
|
|
193
381
|
export declare function formatNodeSummaryTable(nodes: NodeSummary[]): string;
|
|
382
|
+
/**
|
|
383
|
+
* Legend for elided C++ template arguments, emitted only when something in the
|
|
384
|
+
* rendered output actually carries the marker — an unconditional legend is the
|
|
385
|
+
* same token tax the abbreviation exists to remove.
|
|
386
|
+
*/
|
|
387
|
+
export declare function blinkLegend(rendered: Iterable<string>): string;
|
|
194
388
|
export declare function formatQueryNodesResult(result: QueryNodesResult, offset?: number): string;
|
|
389
|
+
/**
|
|
390
|
+
* Build a name filter from a caller-supplied pattern.
|
|
391
|
+
*
|
|
392
|
+
* Interpreted as a case-insensitive regular expression, falling back to a plain
|
|
393
|
+
* case-insensitive substring test when the pattern is not valid regex — so a
|
|
394
|
+
* literal like `blink::UndoStep` works without escaping and a real pattern
|
|
395
|
+
* still works. An empty/absent pattern matches everything.
|
|
396
|
+
*
|
|
397
|
+
* Why this exists: answering "is `blink::UndoStep` present?" previously meant
|
|
398
|
+
* `class_histogram({node_type: "native", limit: 200})` and reading ~4 KB of
|
|
399
|
+
* table for one row. Filtering at the source is the difference between a
|
|
400
|
+
* yes/no answer and a page of output.
|
|
401
|
+
*/
|
|
402
|
+
export declare function makeNamePatternTest(pattern: string | undefined): (name: string) => boolean;
|
|
195
403
|
export declare function snapshotHeader(): string;
|
|
404
|
+
/**
|
|
405
|
+
* The header for a tool that read snapshots given as PATHS.
|
|
406
|
+
*
|
|
407
|
+
* `snapshotHeader()` can only ever describe the RESIDENT snapshot, because that
|
|
408
|
+
* is the only one the session knows about. For a path-taking tool that is the
|
|
409
|
+
* wrong file by construction whenever the caller has something else loaded —
|
|
410
|
+
* and these tools restore the caller's active snapshot before they build their
|
|
411
|
+
* result (see `withSnapshotAt`), so by the time the header is rendered the
|
|
412
|
+
* snapshot it names is never the one that was measured.
|
|
413
|
+
*
|
|
414
|
+
* Measured across a 12-round investigation: every path-based call printed a
|
|
415
|
+
* header naming a capture from an earlier round. A ladder result labelled with
|
|
416
|
+
* another round's rung is a cross-round misattribution hazard in exactly the
|
|
417
|
+
* workflow these tools exist for, so name what was actually read.
|
|
418
|
+
*/
|
|
419
|
+
export declare function pathsHeader(labels: readonly string[]): string;
|
|
196
420
|
export declare function textResult(text: string): {
|
|
197
421
|
content: {
|
|
198
422
|
type: "text";
|
|
199
423
|
text: string;
|
|
200
424
|
}[];
|
|
201
425
|
};
|
|
202
|
-
|
|
426
|
+
/**
|
|
427
|
+
* @param headerOverride Pass `pathsHeader([...])` when the tool read snapshots
|
|
428
|
+
* by PATH rather than operating on the resident one. Passing `null` suppresses
|
|
429
|
+
* the header entirely. Omitting it keeps the session-header behaviour, which is
|
|
430
|
+
* correct only for tools that genuinely act on the active snapshot.
|
|
431
|
+
*/
|
|
432
|
+
export declare function toolResult(text: string, headerOverride?: string | null): {
|
|
203
433
|
content: {
|
|
204
434
|
type: "text";
|
|
205
435
|
text: string;
|
|
@@ -208,9 +438,14 @@ export declare function toolResult(text: string): {
|
|
|
208
438
|
/**
|
|
209
439
|
* Whether tools should omit "Suggested next steps" / "How to fix" trailers.
|
|
210
440
|
* Honors the session-level `suppressSuggestions` config so callers can trim
|
|
211
|
-
* repeated boilerplate tokens across a long investigation
|
|
441
|
+
* repeated boilerplate tokens across a long investigation, and self-suppresses
|
|
442
|
+
* after `SUGGESTION_TRAILER_BUDGET` trailers.
|
|
443
|
+
*
|
|
444
|
+
* NOTE: this both reads and ADVANCES the budget, so call it once per decision.
|
|
212
445
|
*/
|
|
213
446
|
export declare function suggestionsSuppressed(): boolean;
|
|
447
|
+
/** Test seam; also reset when the session config is changed explicitly. */
|
|
448
|
+
export declare function resetSuggestionBudget(): void;
|
|
214
449
|
export declare function jsonResult(data: unknown): {
|
|
215
450
|
content: {
|
|
216
451
|
type: "text";
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACd,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|