@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
package/dist/progress.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
const NOOP = {
|
|
11
|
+
phase: () => {
|
|
12
|
+
// No progress channel and no stderr labelling to do for a caller that
|
|
13
|
+
// supplied no `extra` (in-process dispatch); reporting is simply off.
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Build a reporter from a tool handler's `extra` argument.
|
|
18
|
+
*
|
|
19
|
+
* Tolerates a missing or empty `extra`: tools dispatched in-process by
|
|
20
|
+
* `memlab_batch` are called with `{}`, and a reporter that threw there would
|
|
21
|
+
* turn a progress nicety into a batch-wide failure.
|
|
22
|
+
*/
|
|
23
|
+
export function makeProgressReporter(extra, label) {
|
|
24
|
+
if (extra == null || typeof extra !== 'object')
|
|
25
|
+
return NOOP;
|
|
26
|
+
const e = extra;
|
|
27
|
+
const token = e._meta?.progressToken;
|
|
28
|
+
const send = typeof e.sendNotification === 'function' ? e.sendNotification : null;
|
|
29
|
+
return {
|
|
30
|
+
phase(step, total, message) {
|
|
31
|
+
process.stderr.write(`[${label} ${step}/${total}] ${message}\n`);
|
|
32
|
+
if (send == null || token == null)
|
|
33
|
+
return;
|
|
34
|
+
// Fire-and-forget: a client that cannot accept the notification must not
|
|
35
|
+
// fail the operation the notification is about.
|
|
36
|
+
void send({
|
|
37
|
+
method: 'notifications/progress',
|
|
38
|
+
params: { progressToken: token, progress: step, total, message },
|
|
39
|
+
}).catch(() => {
|
|
40
|
+
// Swallowed on purpose: a client that rejects the notification must not
|
|
41
|
+
// fail the operation the notification is merely describing.
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6CH,MAAM,IAAI,GAAqB;IAC7B,KAAK,EAAE,GAAG,EAAE;QACV,sEAAsE;QACtE,sEAAsE;IACxE,CAAC;CACF,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAc,EACd,KAAa;IAEb,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,CAAC,GAAG,KAA6B,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC;IACrC,MAAM,IAAI,GACR,OAAO,CAAC,CAAC,gBAAgB,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,OAAO;QACL,KAAK,CAAC,IAAY,EAAE,KAAa,EAAE,OAAe;YAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC;YACjE,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI;gBAAE,OAAO;YAC1C,yEAAyE;YACzE,gDAAgD;YAChD,KAAK,IAAI,CAAC;gBACR,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE,EAAC,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAC;aAC/D,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBACZ,wEAAwE;gBACxE,4DAA4D;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* Recognising React structures in a heap snapshot.
|
|
12
|
+
*
|
|
13
|
+
* Fibers, hooks, update queues and update records are ALL plain `Object` in a
|
|
14
|
+
* production bundle, so every class-name-based heuristic walks straight past
|
|
15
|
+
* them. On one population `helpers.owner()` returned `(none)` for 100% of
|
|
16
|
+
* 1,645 records for exactly this reason, and the useful answer —
|
|
17
|
+
* `BaseTooltipSimple` — needed a hand-written walk to the fiber plus a read of
|
|
18
|
+
* `elementType`.
|
|
19
|
+
*
|
|
20
|
+
* That walk has now been hand-written for four findings of the same family
|
|
21
|
+
* across this workstream. The `elementType` / `stateNode` / `memoizedProps`
|
|
22
|
+
* layout is stable across every React version Meta ships, so it belongs here
|
|
23
|
+
* rather than in each investigation.
|
|
24
|
+
*/
|
|
25
|
+
import type { IHeapNode } from '@memlab/core';
|
|
26
|
+
/** Property set of a React update record — the eager-bailout leak family. */
|
|
27
|
+
export declare const UPDATE_RECORD_FIELDS: string[];
|
|
28
|
+
/** Does this node look like a React fiber? */
|
|
29
|
+
export declare function isFiberNode(node: IHeapNode): boolean;
|
|
30
|
+
/** Does this node look like a React update record (`queue.pending` member)? */
|
|
31
|
+
export declare function isUpdateRecord(node: IHeapNode): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The component name behind a fiber.
|
|
34
|
+
*
|
|
35
|
+
* Three shapes, in the order they occur: `elementType` is a closure (function
|
|
36
|
+
* component — the closure's own name is the component name), or a string (host
|
|
37
|
+
* element), or an object carrying `render` / `type` (memo, forwardRef, lazy),
|
|
38
|
+
* in which case the name is one level deeper.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fiberComponentName(fiber: IHeapNode): string | null;
|
|
41
|
+
export declare function nearestFiber(node: IHeapNode, maxHops: number): IHeapNode | null;
|
|
42
|
+
//# sourceMappingURL=react-shapes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-shapes.d.ts","sourceRoot":"","sources":["../src/react-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAe5C,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,UAMhC,CAAC;AAYF,8CAA8C;AAC9C,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAOpD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAWvD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAOlE;AAyCD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,GACd,SAAS,GAAG,IAAI,CAsBlB"}
|
|
@@ -0,0 +1,149 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* Fields that only a fiber carries. `stateNode` alone is too weak (a class
|
|
12
|
+
* instance has one), so a match needs `elementType` or `memoizedProps`, which
|
|
13
|
+
* together with `return`/`child` are fiber-specific.
|
|
14
|
+
*/
|
|
15
|
+
const FIBER_STRONG_FIELDS = ['elementType', 'memoizedProps', 'pendingProps'];
|
|
16
|
+
const FIBER_SUPPORTING_FIELDS = [
|
|
17
|
+
'return',
|
|
18
|
+
'child',
|
|
19
|
+
'stateNode',
|
|
20
|
+
'memoizedState',
|
|
21
|
+
];
|
|
22
|
+
/** Property set of a React update record — the eager-bailout leak family. */
|
|
23
|
+
export const UPDATE_RECORD_FIELDS = [
|
|
24
|
+
'action',
|
|
25
|
+
'eagerState',
|
|
26
|
+
'hasEagerState',
|
|
27
|
+
'lane',
|
|
28
|
+
'next',
|
|
29
|
+
];
|
|
30
|
+
/** Own property names of a node, as a Set. Cheap; used by every check here. */
|
|
31
|
+
function propertyNames(node) {
|
|
32
|
+
const names = new Set();
|
|
33
|
+
for (const edge of node.references) {
|
|
34
|
+
if (edge.type !== 'property')
|
|
35
|
+
continue;
|
|
36
|
+
names.add(String(edge.name_or_index));
|
|
37
|
+
}
|
|
38
|
+
return names;
|
|
39
|
+
}
|
|
40
|
+
/** Does this node look like a React fiber? */
|
|
41
|
+
export function isFiberNode(node) {
|
|
42
|
+
if (node.type !== 'object')
|
|
43
|
+
return false;
|
|
44
|
+
const names = propertyNames(node);
|
|
45
|
+
const strong = FIBER_STRONG_FIELDS.filter(f => names.has(f)).length;
|
|
46
|
+
if (strong === 0)
|
|
47
|
+
return false;
|
|
48
|
+
const supporting = FIBER_SUPPORTING_FIELDS.filter(f => names.has(f)).length;
|
|
49
|
+
return strong + supporting >= 3;
|
|
50
|
+
}
|
|
51
|
+
/** Does this node look like a React update record (`queue.pending` member)? */
|
|
52
|
+
export function isUpdateRecord(node) {
|
|
53
|
+
if (node.type !== 'object')
|
|
54
|
+
return false;
|
|
55
|
+
const names = propertyNames(node);
|
|
56
|
+
// `eagerState` may be absent when the update never took the eager path, so
|
|
57
|
+
// require the rest and treat it as optional.
|
|
58
|
+
return (names.has('action') &&
|
|
59
|
+
names.has('lane') &&
|
|
60
|
+
names.has('next') &&
|
|
61
|
+
(names.has('hasEagerState') || names.has('eagerState')));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The component name behind a fiber.
|
|
65
|
+
*
|
|
66
|
+
* Three shapes, in the order they occur: `elementType` is a closure (function
|
|
67
|
+
* component — the closure's own name is the component name), or a string (host
|
|
68
|
+
* element), or an object carrying `render` / `type` (memo, forwardRef, lazy),
|
|
69
|
+
* in which case the name is one level deeper.
|
|
70
|
+
*/
|
|
71
|
+
export function fiberComponentName(fiber) {
|
|
72
|
+
for (const edge of fiber.references) {
|
|
73
|
+
if (edge.type !== 'property')
|
|
74
|
+
continue;
|
|
75
|
+
if (String(edge.name_or_index) !== 'elementType')
|
|
76
|
+
continue;
|
|
77
|
+
return elementTypeName(edge.toNode, 0);
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function elementTypeName(node, depth) {
|
|
82
|
+
if (depth > 2)
|
|
83
|
+
return null;
|
|
84
|
+
if (node.type === 'string' || node.type === 'concatenated string') {
|
|
85
|
+
const value = node.toStringNode()?.stringValue ?? node.name;
|
|
86
|
+
return value ? `<${value}>` : null;
|
|
87
|
+
}
|
|
88
|
+
if (node.type === 'closure') {
|
|
89
|
+
// A minified bundle still names the closure after the component in the
|
|
90
|
+
// overwhelming majority of cases; an empty name is worth reporting as
|
|
91
|
+
// unknown rather than as "".
|
|
92
|
+
return node.name && node.name !== 'function' ? node.name : null;
|
|
93
|
+
}
|
|
94
|
+
// memo / forwardRef / lazy wrappers.
|
|
95
|
+
for (const edge of node.references) {
|
|
96
|
+
if (edge.type !== 'property')
|
|
97
|
+
continue;
|
|
98
|
+
const name = String(edge.name_or_index);
|
|
99
|
+
if (name !== 'render' && name !== 'type')
|
|
100
|
+
continue;
|
|
101
|
+
const inner = elementTypeName(edge.toNode, depth + 1);
|
|
102
|
+
if (inner != null)
|
|
103
|
+
return inner;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Walk up from a hook/queue/record to the fiber that owns it.
|
|
109
|
+
*
|
|
110
|
+
* Prefers the edges that actually lead there — a hook's owner is reached via
|
|
111
|
+
* `memoizedState` / `next` / `queue` / `baseQueue`, not by whatever referrer
|
|
112
|
+
* happens to come first — and falls back to any referrer so a slightly
|
|
113
|
+
* different shape still resolves.
|
|
114
|
+
*/
|
|
115
|
+
const OWNER_EDGE_PREFERENCE = [
|
|
116
|
+
'memoizedState',
|
|
117
|
+
'queue',
|
|
118
|
+
'baseQueue',
|
|
119
|
+
'next',
|
|
120
|
+
'return',
|
|
121
|
+
];
|
|
122
|
+
export function nearestFiber(node, maxHops) {
|
|
123
|
+
let current = node;
|
|
124
|
+
const seen = new Set([node.id]);
|
|
125
|
+
for (let hop = 0; hop < maxHops; hop++) {
|
|
126
|
+
if (isFiberNode(current))
|
|
127
|
+
return current;
|
|
128
|
+
let chosen = null;
|
|
129
|
+
let fallback = null;
|
|
130
|
+
for (const edge of current.referrers) {
|
|
131
|
+
const from = edge.fromNode;
|
|
132
|
+
if (from.id <= 3 || seen.has(from.id))
|
|
133
|
+
continue;
|
|
134
|
+
if (OWNER_EDGE_PREFERENCE.includes(String(edge.name_or_index))) {
|
|
135
|
+
chosen = from;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
if (fallback == null)
|
|
139
|
+
fallback = from;
|
|
140
|
+
}
|
|
141
|
+
const next = chosen ?? fallback;
|
|
142
|
+
if (next == null)
|
|
143
|
+
return null;
|
|
144
|
+
seen.add(next.id);
|
|
145
|
+
current = next;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=react-shapes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-shapes.js","sourceRoot":"","sources":["../src/react-shapes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
export declare function resolveSavedNodeIds(name: string): number[];
|
|
11
|
+
//# sourceMappingURL=result-handles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-handles.d.ts","sourceRoot":"","sources":["../src/result-handles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuBH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA+C1D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
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 { getEvalScratch } from './heap-state.js';
|
|
11
|
+
/**
|
|
12
|
+
* Read a saved result (from `memlab_eval`'s `save_as` / `helpers.save`) as a
|
|
13
|
+
* node-id list, so one tool's output can feed another's input without the ids
|
|
14
|
+
* making a round trip through the transcript.
|
|
15
|
+
*
|
|
16
|
+
* A list of a few thousand ids is tens of thousands of tokens to print and
|
|
17
|
+
* re-type, and re-typing them is where they get truncated or corrupted. The
|
|
18
|
+
* scratch store already existed for `eval`; this makes it readable by the tools
|
|
19
|
+
* that take `node_ids`, which is where such a list is actually wanted.
|
|
20
|
+
*
|
|
21
|
+
* Accepts the shapes a result realistically has: an array of numbers, an array
|
|
22
|
+
* of `{id}` objects, or an object with an `ids`/`node_ids` array.
|
|
23
|
+
*/
|
|
24
|
+
// `memlab_eval` namespaces saved values in the shared per-snapshot scratch so
|
|
25
|
+
// they cannot collide with its own indexes. Reading them back means honoring
|
|
26
|
+
// that prefix — a bare-name lookup finds nothing and reports "not saved" for a
|
|
27
|
+
// result that is sitting right there.
|
|
28
|
+
const SAVED_PREFIX = '__saved:';
|
|
29
|
+
export function resolveSavedNodeIds(name) {
|
|
30
|
+
const scratch = getEvalScratch();
|
|
31
|
+
const prefixed = SAVED_PREFIX + name;
|
|
32
|
+
const key = prefixed in scratch ? prefixed : name;
|
|
33
|
+
if (!(key in scratch)) {
|
|
34
|
+
const available = Object.keys(scratch)
|
|
35
|
+
.filter(k => k.startsWith(SAVED_PREFIX))
|
|
36
|
+
.map(k => k.slice(SAVED_PREFIX.length));
|
|
37
|
+
throw new Error(`No saved result named "${name}". ` +
|
|
38
|
+
(available.length > 0
|
|
39
|
+
? `Saved: ${available.join(', ')}.`
|
|
40
|
+
: 'Nothing is saved for this snapshot. Save one with memlab_eval({code, save_as: "name"}).') +
|
|
41
|
+
' Saved results are per-snapshot and are dropped when the snapshot is replaced.');
|
|
42
|
+
}
|
|
43
|
+
const raw = scratch[key];
|
|
44
|
+
const fromArray = (arr) => {
|
|
45
|
+
const ids = [];
|
|
46
|
+
for (const item of arr) {
|
|
47
|
+
if (typeof item === 'number' && Number.isFinite(item)) {
|
|
48
|
+
ids.push(item);
|
|
49
|
+
}
|
|
50
|
+
else if (item != null && typeof item === 'object' && 'id' in item) {
|
|
51
|
+
const id = item.id;
|
|
52
|
+
if (typeof id === 'number')
|
|
53
|
+
ids.push(id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return ids;
|
|
57
|
+
};
|
|
58
|
+
let ids = [];
|
|
59
|
+
if (Array.isArray(raw)) {
|
|
60
|
+
ids = fromArray(raw);
|
|
61
|
+
}
|
|
62
|
+
else if (raw != null && typeof raw === 'object') {
|
|
63
|
+
const obj = raw;
|
|
64
|
+
const arr = Array.isArray(obj.ids)
|
|
65
|
+
? obj.ids
|
|
66
|
+
: Array.isArray(obj.node_ids)
|
|
67
|
+
? obj.node_ids
|
|
68
|
+
: null;
|
|
69
|
+
if (arr != null)
|
|
70
|
+
ids = fromArray(arr);
|
|
71
|
+
}
|
|
72
|
+
if (ids.length === 0) {
|
|
73
|
+
throw new Error(`Saved result "${name}" holds no node ids. Expected an array of ids, an array of objects with an \`id\` field, or an object with an \`ids\` array.`);
|
|
74
|
+
}
|
|
75
|
+
return ids;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=result-handles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-handles.js","sourceRoot":"","sources":["../src/result-handles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAE/C;;;;;;;;;;;;GAYG;AACH,8EAA8E;AAC9E,6EAA6E;AAC7E,+EAA+E;AAC/E,sCAAsC;AACtC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEhC,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAClD,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;aACnC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,0BAA0B,IAAI,KAAK;YACjC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;gBACnB,CAAC,CAAC,UAAU,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACnC,CAAC,CAAC,yFAAyF,CAAC;YAC9F,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAY,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,CAAC,GAAc,EAAY,EAAE;QAC7C,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;iBAAM,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACpE,MAAM,EAAE,GAAI,IAAuB,CAAC,EAAE,CAAC;gBACvC,IAAI,OAAO,EAAE,KAAK,QAAQ;oBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,IAAI,GAAG,GAAa,EAAE,CAAC;IACvB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;SAAM,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,GAA0C,CAAC;QACvD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAChC,CAAC,CAAC,GAAG,CAAC,GAAG;YACT,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAC3B,CAAC,CAAC,GAAG,CAAC,QAAQ;gBACd,CAAC,CAAC,IAAI,CAAC;QACX,IAAI,GAAG,IAAI,IAAI;YAAE,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,iBAAiB,IAAI,8HAA8H,CACpJ,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* The version this server reports about itself.
|
|
12
|
+
*
|
|
13
|
+
* Kept in its own module so `memlab_server_status` can report it without
|
|
14
|
+
* importing `index.ts` (which would be an import cycle — index imports every
|
|
15
|
+
* tool). Keep in sync with `package.json`; `scripts/check-version-sync.mjs`
|
|
16
|
+
* enforces that.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SERVER_VERSION = "2.86.0";
|
|
19
|
+
//# sourceMappingURL=server-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-version.d.ts","sourceRoot":"","sources":["../src/server-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* The version this server reports about itself.
|
|
12
|
+
*
|
|
13
|
+
* Kept in its own module so `memlab_server_status` can report it without
|
|
14
|
+
* importing `index.ts` (which would be an import cycle — index imports every
|
|
15
|
+
* tool). Keep in sync with `package.json`; `scripts/check-version-sync.mjs`
|
|
16
|
+
* enforces that.
|
|
17
|
+
*/
|
|
18
|
+
export const SERVER_VERSION = '2.86.0';
|
|
19
|
+
//# sourceMappingURL=server-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-version.js","sourceRoot":"","sources":["../src/server-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
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 type { IHeapSnapshot } from '@memlab/core';
|
|
11
|
+
/**
|
|
12
|
+
* Run `fn` against the snapshot at `localPath`, loading it only if it is not
|
|
13
|
+
* already resident, and leaving the session exactly as it was found.
|
|
14
|
+
*
|
|
15
|
+
* Every tool that walks an ORDERED ladder needs this, and each one getting it
|
|
16
|
+
* subtly wrong is the failure mode worth designing out. Three invariants:
|
|
17
|
+
*
|
|
18
|
+
* - **Reuse a resident graph.** Re-parsing a 250 MB capture that is already in
|
|
19
|
+
* memory is the single most expensive avoidable thing these tools do.
|
|
20
|
+
* - **Hold one graph at a time.** `memlab_eval_across` needs every rung
|
|
21
|
+
* resident simultaneously, which is precisely why it cannot serve a ladder of
|
|
22
|
+
* large captures. Loading and dropping keeps peak RSS at one rung.
|
|
23
|
+
* - **Restore the caller's active snapshot.** A ladder walk is a measurement,
|
|
24
|
+
* not a navigation; silently leaving the session pointed at rung 6 makes the
|
|
25
|
+
* caller's next `memlab_eval` answer a question about the wrong capture, and
|
|
26
|
+
* nothing in that output says so.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withSnapshotAt<T>(localPath: string, fn: (snapshot: IHeapSnapshot) => Promise<T> | T): Promise<T>;
|
|
29
|
+
export interface ResolvedRung {
|
|
30
|
+
/** The path as the caller wrote it, used in report rows. */
|
|
31
|
+
label: string;
|
|
32
|
+
localPath: string;
|
|
33
|
+
sizeMB: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve every path and check its size BEFORE anything is loaded.
|
|
37
|
+
*
|
|
38
|
+
* Ordering matters here: an unreadable or oversized rung five should fail in a
|
|
39
|
+
* second, not after four multi-minute loads have already been paid for.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveRungs(paths: string[], maxFileSizeMB?: number): {
|
|
42
|
+
rungs: ResolvedRung[];
|
|
43
|
+
largestMB: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A full-heap walk on a multi-million-node graph does not finish inside the
|
|
47
|
+
* 60 s eval default, and every caller discovering that by timing out first is
|
|
48
|
+
* pure friction. Scale from the largest rung.
|
|
49
|
+
*/
|
|
50
|
+
export declare function scaledTimeoutMs(largestMB: number, explicit?: number): number;
|
|
51
|
+
/**
|
|
52
|
+
* Re-arm the whole-heap scan guardrail for the work about to be done.
|
|
53
|
+
*
|
|
54
|
+
* Scaling a tool's own `timeout_ms` is NOT enough on its own, which is only
|
|
55
|
+
* obvious once you run against a big graph. The guardrail in `guardrail.ts`
|
|
56
|
+
* arms a 90s scan budget from the tool's incoming params before the handler
|
|
57
|
+
* runs; a tool that then computes a larger per-snapshot timeout is still inside
|
|
58
|
+
* that 90s budget, so a full-heap walk dies at 90s no matter what it asked for.
|
|
59
|
+
* Measured on a real 690 MB / 5.1M-node Ads Manager capture: a detached-node
|
|
60
|
+
* probe raised its eval timeout to 414s and still lost the rung to
|
|
61
|
+
* `Scan exceeded its 90000ms budget`.
|
|
62
|
+
*
|
|
63
|
+
* Re-arming per snapshot is what `memlab_analyze_run` already does for its
|
|
64
|
+
* steps. The guardrail's own `finally` disarms afterwards, so nothing leaks.
|
|
65
|
+
*/
|
|
66
|
+
export declare function armScanBudgetFor(timeoutMs: number): void;
|
|
67
|
+
//# sourceMappingURL=snapshot-borrow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-borrow.d.ts","sourceRoot":"","sources":["../src/snapshot-borrow.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAkBhD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CA+BZ;AAED,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EAAE,EACf,aAAa,CAAC,EAAE,MAAM,GACrB;IAAC,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,CAoB5C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAExD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
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 fs from 'fs';
|
|
11
|
+
import memlabHeapAnalysis from '@memlab/heap-analysis';
|
|
12
|
+
const { getFullHeapFromFile } = memlabHeapAnalysis;
|
|
13
|
+
import { findResidentByPath, getCurrentHandle, getSnapshotByHandle, removeSnapshot, setCurrentSnapshot, setSnapshot, } from './heap-state.js';
|
|
14
|
+
import { beginAnalysisBudget } from './analysis-budget.js';
|
|
15
|
+
import { resolveMaxFileSizeMB, resolveSnapshotPath, } from './tools/load-snapshot.js';
|
|
16
|
+
/**
|
|
17
|
+
* Run `fn` against the snapshot at `localPath`, loading it only if it is not
|
|
18
|
+
* already resident, and leaving the session exactly as it was found.
|
|
19
|
+
*
|
|
20
|
+
* Every tool that walks an ORDERED ladder needs this, and each one getting it
|
|
21
|
+
* subtly wrong is the failure mode worth designing out. Three invariants:
|
|
22
|
+
*
|
|
23
|
+
* - **Reuse a resident graph.** Re-parsing a 250 MB capture that is already in
|
|
24
|
+
* memory is the single most expensive avoidable thing these tools do.
|
|
25
|
+
* - **Hold one graph at a time.** `memlab_eval_across` needs every rung
|
|
26
|
+
* resident simultaneously, which is precisely why it cannot serve a ladder of
|
|
27
|
+
* large captures. Loading and dropping keeps peak RSS at one rung.
|
|
28
|
+
* - **Restore the caller's active snapshot.** A ladder walk is a measurement,
|
|
29
|
+
* not a navigation; silently leaving the session pointed at rung 6 makes the
|
|
30
|
+
* caller's next `memlab_eval` answer a question about the wrong capture, and
|
|
31
|
+
* nothing in that output says so.
|
|
32
|
+
*/
|
|
33
|
+
export async function withSnapshotAt(localPath, fn) {
|
|
34
|
+
const resident = findResidentByPath(localPath);
|
|
35
|
+
let snapshot = resident != null ? getSnapshotByHandle(resident.handle) : null;
|
|
36
|
+
const previousHandle = getCurrentHandle();
|
|
37
|
+
let temporaryHandle = null;
|
|
38
|
+
try {
|
|
39
|
+
if (snapshot == null) {
|
|
40
|
+
snapshot = await getFullHeapFromFile(localPath);
|
|
41
|
+
const meta = setSnapshot(snapshot, localPath, {
|
|
42
|
+
fileName: localPath.replace(/^.*\//, ''),
|
|
43
|
+
nodeCount: snapshot.nodes?.length ?? 0,
|
|
44
|
+
edgeCount: snapshot.edges?.length ?? 0,
|
|
45
|
+
totalSize: 0,
|
|
46
|
+
env: 'unknown',
|
|
47
|
+
}, { replace: false });
|
|
48
|
+
temporaryHandle = meta.handle;
|
|
49
|
+
}
|
|
50
|
+
else if (resident != null) {
|
|
51
|
+
setCurrentSnapshot(resident.handle);
|
|
52
|
+
}
|
|
53
|
+
return await fn(snapshot);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
if (temporaryHandle != null)
|
|
57
|
+
removeSnapshot(temporaryHandle);
|
|
58
|
+
if (previousHandle != null)
|
|
59
|
+
setCurrentSnapshot(previousHandle);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve every path and check its size BEFORE anything is loaded.
|
|
64
|
+
*
|
|
65
|
+
* Ordering matters here: an unreadable or oversized rung five should fail in a
|
|
66
|
+
* second, not after four multi-minute loads have already been paid for.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveRungs(paths, maxFileSizeMB) {
|
|
69
|
+
const rungs = [];
|
|
70
|
+
let largestMB = 0;
|
|
71
|
+
for (const p of paths) {
|
|
72
|
+
const { localPath, fetchedFrom } = resolveSnapshotPath(p);
|
|
73
|
+
if (!fs.existsSync(localPath)) {
|
|
74
|
+
throw new Error(`File not found: ${localPath}`);
|
|
75
|
+
}
|
|
76
|
+
const sizeMB = fs.statSync(localPath).size / (1024 * 1024);
|
|
77
|
+
largestMB = Math.max(largestMB, sizeMB);
|
|
78
|
+
const limit = resolveMaxFileSizeMB(maxFileSizeMB, fetchedFrom != null);
|
|
79
|
+
if (sizeMB > limit) {
|
|
80
|
+
throw new Error(`${p} is ${sizeMB.toFixed(0)} MB — exceeds the ${limit} MB per-file ` +
|
|
81
|
+
'limit. Raise it with max_file_size_mb.');
|
|
82
|
+
}
|
|
83
|
+
rungs.push({ label: p.replace(/^.*\//, ''), localPath, sizeMB });
|
|
84
|
+
}
|
|
85
|
+
return { rungs, largestMB };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A full-heap walk on a multi-million-node graph does not finish inside the
|
|
89
|
+
* 60 s eval default, and every caller discovering that by timing out first is
|
|
90
|
+
* pure friction. Scale from the largest rung.
|
|
91
|
+
*/
|
|
92
|
+
export function scaledTimeoutMs(largestMB, explicit) {
|
|
93
|
+
return explicit ?? Math.max(60000, Math.ceil(largestMB * 600));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Re-arm the whole-heap scan guardrail for the work about to be done.
|
|
97
|
+
*
|
|
98
|
+
* Scaling a tool's own `timeout_ms` is NOT enough on its own, which is only
|
|
99
|
+
* obvious once you run against a big graph. The guardrail in `guardrail.ts`
|
|
100
|
+
* arms a 90s scan budget from the tool's incoming params before the handler
|
|
101
|
+
* runs; a tool that then computes a larger per-snapshot timeout is still inside
|
|
102
|
+
* that 90s budget, so a full-heap walk dies at 90s no matter what it asked for.
|
|
103
|
+
* Measured on a real 690 MB / 5.1M-node Ads Manager capture: a detached-node
|
|
104
|
+
* probe raised its eval timeout to 414s and still lost the rung to
|
|
105
|
+
* `Scan exceeded its 90000ms budget`.
|
|
106
|
+
*
|
|
107
|
+
* Re-arming per snapshot is what `memlab_analyze_run` already does for its
|
|
108
|
+
* steps. The guardrail's own `finally` disarms afterwards, so nothing leaks.
|
|
109
|
+
*/
|
|
110
|
+
export function armScanBudgetFor(timeoutMs) {
|
|
111
|
+
beginAnalysisBudget(timeoutMs);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=snapshot-borrow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-borrow.js","sourceRoot":"","sources":["../src/snapshot-borrow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 type { IHeapNode, IHeapSnapshot } from '@memlab/core';
|
|
11
|
+
/**
|
|
12
|
+
* Per-value string duplication stats: how many copies, what they cost, and a
|
|
13
|
+
* bounded sample of node ids to follow up on.
|
|
14
|
+
*/
|
|
15
|
+
export interface StringDupStats {
|
|
16
|
+
count: number;
|
|
17
|
+
totalSize: number;
|
|
18
|
+
exampleIds: number[];
|
|
19
|
+
/**
|
|
20
|
+
* Copies reachable ONLY through a dev/automation root. -1 when the index was
|
|
21
|
+
* built without the dev-root pass (see {@link buildStringIndex}).
|
|
22
|
+
*/
|
|
23
|
+
devOnlyCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface StringIndex {
|
|
26
|
+
byValue: Map<string, StringDupStats>;
|
|
27
|
+
/** True when `devOnlyCount` is populated. */
|
|
28
|
+
hasDevOnly: boolean;
|
|
29
|
+
concatStringCount: number;
|
|
30
|
+
concatStringSize: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build (or reuse) the per-value string duplication index for a snapshot.
|
|
34
|
+
*
|
|
35
|
+
* `memlab_duplicated_strings` and `memlab_intern_opportunities` were each doing
|
|
36
|
+
* their own full pass over every string node to build the same value → {count,
|
|
37
|
+
* size, ids} map, and both are commonly run back to back on the same snapshot —
|
|
38
|
+
* so the second one re-walked millions of nodes to recompute what the first had
|
|
39
|
+
* already produced. The scan is cached in the per-snapshot eval scratch, which
|
|
40
|
+
* already has exactly the lifecycle this needs: keyed by the active handle, and
|
|
41
|
+
* dropped when that snapshot is replaced or unloaded, so a stale index can never
|
|
42
|
+
* outlive the graph its node ids refer to.
|
|
43
|
+
*
|
|
44
|
+
* `withDevOnly` gates the dev/automation reachability pass, which is a whole-
|
|
45
|
+
* graph walk that only `intern_opportunities` needs. A cached index built
|
|
46
|
+
* without it is UPGRADED (rebuilt once) rather than silently returned, since
|
|
47
|
+
* `devOnlyCount: -1` would otherwise be read as "no dev-only copies".
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildStringIndex(snapshot: IHeapSnapshot, opts?: {
|
|
50
|
+
withDevOnly?: boolean;
|
|
51
|
+
isDevOnlyNode?: (node: IHeapNode) => boolean;
|
|
52
|
+
}): StringIndex;
|
|
53
|
+
/** True when this snapshot's string index is already built and resident. */
|
|
54
|
+
export declare function stringIndexIsCached(): boolean;
|
|
55
|
+
//# sourceMappingURL=string-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-index.d.ts","sourceRoot":"","sources":["../src/string-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAE,aAAa,EAAC,MAAM,cAAc,CAAC;AAG3D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAQD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,EACvB,IAAI,GAAE;IACJ,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;CACzC,GACL,WAAW,CA8Db;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C"}
|