@memlab/mcp-server 2.52.0 → 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 +22 -2
- 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/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/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 +20 -0
- package/dist/heap-state.d.ts.map +1 -1
- package/dist/heap-state.js +8 -1
- package/dist/heap-state.js.map +1 -1
- package/dist/index.js +64 -1
- package/dist/index.js.map +1 -1
- 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/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.map +1 -1
- package/dist/string-index.js +8 -0
- package/dist/string-index.js.map +1 -1
- package/dist/tools/analyze-run.d.ts.map +1 -1
- package/dist/tools/analyze-run.js +79 -18
- package/dist/tools/analyze-run.js.map +1 -1
- 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 +18 -0
- package/dist/tools/app-heap.d.ts.map +1 -1
- package/dist/tools/app-heap.js +12 -46
- package/dist/tools/app-heap.js.map +1 -1
- 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.map +1 -1
- package/dist/tools/batch.js +33 -5
- package/dist/tools/batch.js.map +1 -1
- 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 +85 -10
- 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/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.map +1 -1
- package/dist/tools/collection-trend.js +2 -2
- package/dist/tools/collection-trend.js.map +1 -1
- package/dist/tools/detached-dom.d.ts +3 -0
- package/dist/tools/detached-dom.d.ts.map +1 -1
- package/dist/tools/detached-dom.js +78 -3
- package/dist/tools/detached-dom.js.map +1 -1
- package/dist/tools/dev-artifacts.d.ts.map +1 -1
- package/dist/tools/dev-artifacts.js +221 -3
- 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/duplicated-strings.d.ts.map +1 -1
- package/dist/tools/duplicated-strings.js +3 -2
- 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 +78 -0
- package/dist/tools/eval.d.ts.map +1 -1
- package/dist/tools/eval.js +2270 -701
- 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.map +1 -1
- package/dist/tools/explain-delta.js +61 -10
- package/dist/tools/explain-delta.js.map +1 -1
- package/dist/tools/finding-index.d.ts +181 -0
- package/dist/tools/finding-index.d.ts.map +1 -1
- package/dist/tools/finding-index.js +343 -24
- package/dist/tools/finding-index.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/hypothesis.d.ts.map +1 -1
- package/dist/tools/hypothesis.js +117 -58
- package/dist/tools/hypothesis.js.map +1 -1
- 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 +4 -3
- 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.map +1 -1
- package/dist/tools/ladder.js +5 -5
- package/dist/tools/ladder.js.map +1 -1
- package/dist/tools/leak-report.d.ts +23 -0
- package/dist/tools/leak-report.d.ts.map +1 -1
- package/dist/tools/leak-report.js +153 -11
- package/dist/tools/leak-report.js.map +1 -1
- package/dist/tools/load-snapshot.d.ts +11 -0
- package/dist/tools/load-snapshot.d.ts.map +1 -1
- package/dist/tools/load-snapshot.js +83 -10
- package/dist/tools/load-snapshot.js.map +1 -1
- 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/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-summary.d.ts +17 -0
- package/dist/tools/retainer-summary.d.ts.map +1 -1
- package/dist/tools/retainer-summary.js +85 -22
- 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 +11 -1
- 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/sequence-analysis.d.ts +4 -0
- package/dist/tools/sequence-analysis.d.ts.map +1 -1
- package/dist/tools/sequence-analysis.js +53 -9
- 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/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 +2 -1
- package/dist/tools/string-patterns.js.map +1 -1
- package/dist/tools/tools-index.d.ts.map +1 -1
- package/dist/tools/tools-index.js +155 -5
- package/dist/tools/tools-index.js.map +1 -1
- 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.map +1 -1
- package/dist/tools/verify-fix.js +11 -3
- package/dist/tools/verify-fix.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 +92 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +172 -5
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -96,7 +96,11 @@ To auto-approve the read-only tools while keeping the two code-execution tools o
|
|
|
96
96
|
|
|
97
97
|
## How It Works
|
|
98
98
|
|
|
99
|
-
The server holds
|
|
99
|
+
The server holds loaded `IHeapSnapshot` graphs in memory across tool calls (loading is
|
|
100
|
+
expensive for large heaps). Several snapshots can be resident at once (`keep_previous: true`);
|
|
101
|
+
tools read the active one, which `memlab_snapshots` switches. When a load's projected working
|
|
102
|
+
set exceeds the free old-space budget, the least-recently-used resident snapshot is evicted
|
|
103
|
+
before the load is refused. All tools are read-only — they analyze the heap but never modify it.
|
|
100
104
|
|
|
101
105
|
## Getting a Heap Snapshot
|
|
102
106
|
|
|
@@ -120,11 +124,14 @@ console.log(`Heap snapshot written to ${snapshot}`);
|
|
|
120
124
|
### `memlab_load_snapshot`
|
|
121
125
|
|
|
122
126
|
Load and parse a `.heapsnapshot` file. Builds indexes, computes the dominator tree, and calculates retained sizes. `file_path` may be a local absolute path, a `manifold://bucket/key` URL, or a bare snapshot filename (resolved against the `nest_server_nodejs_heap_snapshots` bucket and fetched via `manifold get`). Pass `keep_previous: true` to keep earlier snapshots resident for diffing/comparison (each gets a handle; manage with `memlab_snapshots`). `quiet` / `suppress_suggestions` set session-wide output controls to trim repeated boilerplate.
|
|
127
|
+
Pass `light: true` for a count-only load that skips the dominator / retained-size /
|
|
128
|
+
shortest-path pass; tools needing retained sizes, dominators or retainer paths then return an
|
|
129
|
+
error naming the light-safe tools instead of reporting zeros.
|
|
123
130
|
|
|
124
131
|
```
|
|
125
132
|
Input: { file_path: "snap.heapsnapshot" | "/abs/path" | "manifold://bucket/key",
|
|
126
133
|
alias?: "before", keep_previous?: false, quiet?: false,
|
|
127
|
-
suppress_suggestions?: false, max_file_size_mb?: 900 }
|
|
134
|
+
suppress_suggestions?: false, light?: false, max_file_size_mb?: 900 }
|
|
128
135
|
Output: { status, file_path, node_count, edge_count, total_size, handle }
|
|
129
136
|
```
|
|
130
137
|
|
|
@@ -148,6 +155,19 @@ Input: { action?: "list"|"switch"|"unload", handle?: "before",
|
|
|
148
155
|
Output: resident snapshots (active one marked), or switch/unload result
|
|
149
156
|
```
|
|
150
157
|
|
|
158
|
+
### `memlab_batch`
|
|
159
|
+
|
|
160
|
+
Run several tools against ONE snapshot load and return all their outputs together, instead of
|
|
161
|
+
re-paying the multi-minute parse on every call. Steps run in order against the resident snapshot
|
|
162
|
+
and see each other's side effects. An unknown tool name is rejected before any step runs, and by
|
|
163
|
+
default a failing step is recorded and the batch continues.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
Input: { load?: { ...memlab_load_snapshot args }, steps: [{ tool, args? }],
|
|
167
|
+
stop_on_error?: false }
|
|
168
|
+
Output: a per-step output block for each step, headed by a run/failed step count
|
|
169
|
+
```
|
|
170
|
+
|
|
151
171
|
### `memlab_property_distribution`
|
|
152
172
|
|
|
153
173
|
For a class/shape and a property, report value cardinality plus the top-K most frequent values. The key tool for diagnosing cardinality explosions (OTel metric attributes, cache keys, per-record fields).
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
* Has this capture had its string content replaced — and if so, in a way that
|
|
13
|
+
* corrupts the duplication numbers, or not?
|
|
14
|
+
*
|
|
15
|
+
* Anonymisers replace string CONTENT while preserving LENGTH. Whether that
|
|
16
|
+
* matters to an analysis depends entirely on which kind you are looking at, so
|
|
17
|
+
* this reports three states rather than a boolean:
|
|
18
|
+
*
|
|
19
|
+
* - `uniform` — every character replaced by ONE filler character. Every
|
|
20
|
+
* distinct string of a given length collapses into the same value, so every
|
|
21
|
+
* string-duplication analysis measures the anonymiser instead of the app.
|
|
22
|
+
* Measured on one such capture: `memlab_quick_diagnosis` reported **"Total
|
|
23
|
+
* interning savings: 47.4 MB"** and `memlab_duplicated_strings` reported a
|
|
24
|
+
* single 5.2 KB string duplicated 939 times for 42.1 MB. Both were pure
|
|
25
|
+
* artifact. Nothing flagged it; it was caught only because the sample values
|
|
26
|
+
* rendered visibly as runs of `?`.
|
|
27
|
+
* - `stable` — replaced by a value-derived token, so distinctness survives
|
|
28
|
+
* along with length. Duplication and interning figures are as true as on the
|
|
29
|
+
* original capture, and warning about them would make readers discard
|
|
30
|
+
* correct findings. Only the content is unreadable.
|
|
31
|
+
* - `none` — an ordinary capture.
|
|
32
|
+
*
|
|
33
|
+
* Both tests are deliberately cheap and deliberately conservative: sample
|
|
34
|
+
* strings, and only classify when a large majority match. A normal heap has
|
|
35
|
+
* plenty of short repeated-character strings (" ", "----") and plenty of short
|
|
36
|
+
* lowercase words, so the length floor matters more than the ratio for either.
|
|
37
|
+
*/
|
|
38
|
+
export type AnonymizationKind =
|
|
39
|
+
/** ordinary capture */
|
|
40
|
+
'none'
|
|
41
|
+
/**
|
|
42
|
+
* every character replaced by one filler character. Length survives, so
|
|
43
|
+
* distinct values of equal length collapse into one and duplication figures
|
|
44
|
+
* become fiction.
|
|
45
|
+
*/
|
|
46
|
+
| 'uniform'
|
|
47
|
+
/**
|
|
48
|
+
* replaced by a value-derived token. Length AND distinctness survive, so
|
|
49
|
+
* duplication figures stay true; only the content is gone.
|
|
50
|
+
*/
|
|
51
|
+
| 'stable';
|
|
52
|
+
export interface AnonymizationCheck {
|
|
53
|
+
kind: AnonymizationKind;
|
|
54
|
+
/**
|
|
55
|
+
* true only for `uniform`, which is the kind that corrupts duplication
|
|
56
|
+
* numbers. Existing call sites read this to decide whether to warn, and a
|
|
57
|
+
* `stable` capture must NOT trip them — its numbers are trustworthy.
|
|
58
|
+
*/
|
|
59
|
+
anonymized: boolean;
|
|
60
|
+
sampled: number;
|
|
61
|
+
singleCharRuns: number;
|
|
62
|
+
tokenRuns: number;
|
|
63
|
+
exampleChar?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function detectAnonymizedStrings(snapshot: IHeapSnapshot): AnonymizationCheck;
|
|
66
|
+
/** The banner every string-content tool prints when the capture is anonymised. */
|
|
67
|
+
export declare function anonymizedStringsBanner(check: AnonymizationCheck): string;
|
|
68
|
+
/**
|
|
69
|
+
* Prepend the anonymisation banner to a tool's output when it applies.
|
|
70
|
+
*
|
|
71
|
+
* Wraps the result rather than being left to each caller to remember, because
|
|
72
|
+
* "remember to check" is exactly the failure this exists to prevent.
|
|
73
|
+
*/
|
|
74
|
+
export declare function withAnonymizedBanner(snapshot: IHeapSnapshot, text: string): string;
|
|
75
|
+
//# sourceMappingURL=anonymized-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anonymized-snapshot.d.ts","sourceRoot":"","sources":["../src/anonymized-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,iBAAiB;AAC3B,uBAAuB;AACrB,MAAM;AACR;;;;GAIG;GACD,SAAS;AACX;;;GAGG;GACD,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,GACtB,kBAAkB,CAqDpB;AAED,kFAAkF;AAClF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAwBzE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EACvB,IAAI,EAAE,MAAM,GACX,MAAM,CAIR"}
|
|
@@ -0,0 +1,101 @@
|
|
|
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 MIN_LEN = 8;
|
|
11
|
+
const SAMPLE_TARGET = 400;
|
|
12
|
+
const RATIO = 0.8;
|
|
13
|
+
export function detectAnonymizedStrings(snapshot) {
|
|
14
|
+
let sampled = 0;
|
|
15
|
+
let singleCharRuns = 0;
|
|
16
|
+
let tokenRuns = 0;
|
|
17
|
+
let exampleChar;
|
|
18
|
+
let visited = 0;
|
|
19
|
+
snapshot.nodes.forEach(node => {
|
|
20
|
+
if (sampled >= SAMPLE_TARGET)
|
|
21
|
+
return;
|
|
22
|
+
if (node.type !== 'string')
|
|
23
|
+
return;
|
|
24
|
+
// Stride rather than take the first N: the first strings in a heap are
|
|
25
|
+
// V8 internals and interned literals, which are never anonymised and would
|
|
26
|
+
// make every capture look clean.
|
|
27
|
+
visited++;
|
|
28
|
+
if (visited % 7 !== 0)
|
|
29
|
+
return;
|
|
30
|
+
const value = node.name;
|
|
31
|
+
if (typeof value !== 'string' || value.length < MIN_LEN)
|
|
32
|
+
return;
|
|
33
|
+
sampled++;
|
|
34
|
+
const first = value[0];
|
|
35
|
+
let uniform = true;
|
|
36
|
+
for (let i = 1; i < value.length; i++) {
|
|
37
|
+
if (value[i] !== first) {
|
|
38
|
+
uniform = false;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (uniform) {
|
|
43
|
+
singleCharRuns++;
|
|
44
|
+
exampleChar ??= first;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// `stable` fill is a run of lowercase letters and nothing else. Real string
|
|
48
|
+
// values of this length essentially always carry a space, digit, capital or
|
|
49
|
+
// punctuation mark somewhere, so a heap where most of them are bare
|
|
50
|
+
// lowercase runs has been rewritten.
|
|
51
|
+
if (/^[a-z]+$/.test(value)) {
|
|
52
|
+
tokenRuns++;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const uniformRatio = sampled >= 20 ? singleCharRuns / sampled : 0;
|
|
56
|
+
const tokenRatio = sampled >= 20 ? tokenRuns / sampled : 0;
|
|
57
|
+
const kind = uniformRatio >= RATIO ? 'uniform' : tokenRatio >= RATIO ? 'stable' : 'none';
|
|
58
|
+
return {
|
|
59
|
+
kind,
|
|
60
|
+
anonymized: kind === 'uniform',
|
|
61
|
+
sampled,
|
|
62
|
+
singleCharRuns,
|
|
63
|
+
tokenRuns,
|
|
64
|
+
exampleChar,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** The banner every string-content tool prints when the capture is anonymised. */
|
|
68
|
+
export function anonymizedStringsBanner(check) {
|
|
69
|
+
if (check.kind === 'stable') {
|
|
70
|
+
// Deliberately NOT a warning about the numbers. `stable` anonymization
|
|
71
|
+
// preserves distinctness, so duplication and interning figures are exactly
|
|
72
|
+
// as true as on the original capture — saying otherwise here would make
|
|
73
|
+
// readers discard correct findings.
|
|
74
|
+
const pct = check.sampled
|
|
75
|
+
? Math.round((check.tokenRuns / check.sampled) * 100)
|
|
76
|
+
: 0;
|
|
77
|
+
return (`> ℹ️ **This capture is ANONYMISED (value-stable) — ${pct}% of sampled string values are opaque tokens.** ` +
|
|
78
|
+
'String CONTENT has been replaced, but lengths and distinctness were preserved, so counts, sizes, duplication and interning figures below are accurate. ' +
|
|
79
|
+
'What you cannot do is read the strings: a sample value tells you nothing about the real data, so identify records by property shape rather than by value.');
|
|
80
|
+
}
|
|
81
|
+
const pct = check.sampled
|
|
82
|
+
? Math.round((check.singleCharRuns / check.sampled) * 100)
|
|
83
|
+
: 0;
|
|
84
|
+
return (`> ⚠️ **This capture looks ANONYMISED — ${pct}% of sampled strings are a single character (\`${check.exampleChar ?? '?'}\`) repeated.** ` +
|
|
85
|
+
'An anonymiser replaces string content but preserves length, so every distinct string of the same length collapses to the same value. ' +
|
|
86
|
+
'**Every duplication and interning figure below is manufactured by that collapse and does not exist in the real app.** ' +
|
|
87
|
+
'Do not quote them, and do not open an interning task from them. Composition, populations, retainer paths and dominator-deduped sizes are unaffected and remain trustworthy.');
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Prepend the anonymisation banner to a tool's output when it applies.
|
|
91
|
+
*
|
|
92
|
+
* Wraps the result rather than being left to each caller to remember, because
|
|
93
|
+
* "remember to check" is exactly the failure this exists to prevent.
|
|
94
|
+
*/
|
|
95
|
+
export function withAnonymizedBanner(snapshot, text) {
|
|
96
|
+
const check = detectAnonymizedStrings(snapshot);
|
|
97
|
+
if (check.kind === 'none')
|
|
98
|
+
return text;
|
|
99
|
+
return `${anonymizedStringsBanner(check)}\n\n${text}`;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=anonymized-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anonymized-snapshot.js","sourceRoot":"","sources":["../src/anonymized-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
* Recognise an eval that is re-implementing a tool that already exists, and say
|
|
12
|
+
* so alongside the result.
|
|
13
|
+
*
|
|
14
|
+
* The discoverability problem this addresses is measured, not hypothetical: in
|
|
15
|
+
* one long session `memlab_eval_across`, `memlab_population_diff`,
|
|
16
|
+
* `memlab_event_registry`, `memlab_collection_trend` and `memlab_verify_fix`
|
|
17
|
+
* all existed, all would have saved work, and none was called — while the same
|
|
18
|
+
* questions were answered with hand-written evals, several times each. The
|
|
19
|
+
* operator had even read the warning about exactly this and still did not open
|
|
20
|
+
* the catalogue, because under time pressure writing the eval is the shorter
|
|
21
|
+
* path and nothing in that path mentions the alternative.
|
|
22
|
+
*
|
|
23
|
+
* So the hint is attached to the RESULT of the call that made the mistake,
|
|
24
|
+
* which is the one moment the caller is guaranteed to read. Two rules keep it
|
|
25
|
+
* from becoming noise:
|
|
26
|
+
*
|
|
27
|
+
* - **Never suppress or alter the result.** The eval ran; the hint is a
|
|
28
|
+
* footnote. A hint that blocked or replaced output would be worse than the
|
|
29
|
+
* duplication it is trying to prevent.
|
|
30
|
+
* - **Match on structure, not vocabulary.** A pattern fires only when the code
|
|
31
|
+
* shows the SHAPE of the built-in's job (a `{callback, context}` census, a
|
|
32
|
+
* per-class count keyed by snapshot, a detached-node distribution), not
|
|
33
|
+
* merely because it mentions a word.
|
|
34
|
+
*/
|
|
35
|
+
export interface EvalHint {
|
|
36
|
+
tool: string;
|
|
37
|
+
why: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function hintsForEval(code: string): EvalHint[];
|
|
40
|
+
export declare function formatEvalHints(hints: EvalHint[]): string | null;
|
|
41
|
+
//# sourceMappingURL=eval-hints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval-hints.d.ts","sourceRoot":"","sources":["../src/eval-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAkGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CASrD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,GAAG,IAAI,CAKhE"}
|
|
@@ -0,0 +1,115 @@
|
|
|
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 RULES = [
|
|
11
|
+
{
|
|
12
|
+
tool: 'memlab_event_registry',
|
|
13
|
+
why: 'counts `{callback, context}` listener records by event and host directly',
|
|
14
|
+
// `callback` and `context` are two of the most common identifiers in
|
|
15
|
+
// JavaScript, so matching them alone fires on evals that have nothing to do
|
|
16
|
+
// with listeners. Require the SHAPE as well: both read as properties off
|
|
17
|
+
// the same record, and the eval is accumulating or iterating over them.
|
|
18
|
+
all: [
|
|
19
|
+
/[.[]['"]?callback\b|\bcallback\s*:/,
|
|
20
|
+
/[.[]['"]?context\b|\bcontext\s*:/,
|
|
21
|
+
/&&|\+\+|\+= ?1|\|\| ?0\) ?\+ ?1|\?\? ?0\) ?\+ ?1|\.push\(|\bfor\b/,
|
|
22
|
+
],
|
|
23
|
+
// Already going through the census tool, or bucketing something else.
|
|
24
|
+
none: [/memlab_event_registry/],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
tool: 'memlab_detached_dom',
|
|
28
|
+
why: 'reports the detached-node population and its per-class distribution, using the same pinned/GC-eligible split the other tools use',
|
|
29
|
+
all: [/is_detached|isRealDetached|Detached </],
|
|
30
|
+
none: [/memlab_detached_dom/],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
tool: 'memlab_census_diff',
|
|
34
|
+
why: 'takes the detached and listener census on BOTH rungs and diffs them per class in one call — no manual map-building or unload/reload cycle',
|
|
35
|
+
all: [
|
|
36
|
+
/is_detached|isRealDetached|Detached </,
|
|
37
|
+
/\bbaseline\b|\bbefore\b|\bprev\b/,
|
|
38
|
+
],
|
|
39
|
+
none: [/memlab_census_diff/],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
tool: 'memlab_class_histogram',
|
|
43
|
+
why: 'produces the per-class count/size table without a hand-written walk',
|
|
44
|
+
// Deliberately not keyed to the identifier `node`: the callback parameter
|
|
45
|
+
// is as often `n`, and requiring the long spelling made the rule silently
|
|
46
|
+
// never fire on the shape it exists to catch.
|
|
47
|
+
all: [
|
|
48
|
+
/snapshot\.nodes\.forEach|for\s*\(\s*const\s+\w+\s+of\s+snapshot\.nodes/,
|
|
49
|
+
/\.name\b/,
|
|
50
|
+
/\+\+|\+= ?1|\|\| ?0\) ?\+ ?1|\?\? ?0\) ?\+ ?1/,
|
|
51
|
+
],
|
|
52
|
+
none: [/memlab_class_histogram|helpers\.classCounts/],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
tool: 'memlab_ladder_probe',
|
|
56
|
+
why: 'runs ONE numeric probe across an ordered ladder and returns the series, the per-cycle rate and r² — loading one rung at a time, so it works on captures too large to hold simultaneously',
|
|
57
|
+
all: [/helpers\.(save|load)\(|__rung|perRung|per_rung/],
|
|
58
|
+
none: [/memlab_ladder_probe/],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
tool: 'memlab_duplicate_objects',
|
|
62
|
+
why: 'is a ready-made dedup report over object content signatures',
|
|
63
|
+
all: [/shapeSignature|objectContentSignature/, /\bMap\b|\bSet\b/],
|
|
64
|
+
none: [/memlab_duplicate_objects/],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
// One canonical tool name per hint. The pair used to be crammed into this
|
|
68
|
+
// single field, which rendered as one backticked identifier containing a
|
|
69
|
+
// slash and spaces — not a name any caller or renderer can resolve. The
|
|
70
|
+
// sibling tool goes in `why`, as prose, where it belongs.
|
|
71
|
+
tool: 'memlab_map_entries',
|
|
72
|
+
why: 'enumerates collection entries without the backing-store walk (see `memlab_weakmap_entries` for WeakMaps)',
|
|
73
|
+
all: [/table|backing/i, /\bMap\b|\bWeakMap\b/],
|
|
74
|
+
none: [
|
|
75
|
+
/helpers\.mapEntries|helpers\.weakmapEntries/,
|
|
76
|
+
/memlab_map_entries|memlab_weakmap_entries/,
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
tool: 'memlab_retainer_trace',
|
|
81
|
+
why: 'walks retainer paths with framework filtering and path collapsing already applied',
|
|
82
|
+
all: [
|
|
83
|
+
/\.referrers\b/,
|
|
84
|
+
/while\s*\(|for\s*\(/,
|
|
85
|
+
/pathEdge|dominatorNode|\.referrers\[0\]/,
|
|
86
|
+
],
|
|
87
|
+
none: [/memlab_retainer_trace|helpers\.pathBetween/],
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* At most this many hints per call. Beyond two the footnote stops being a
|
|
92
|
+
* pointer and starts being a second result the caller has to read.
|
|
93
|
+
*/
|
|
94
|
+
const MAX_HINTS = 2;
|
|
95
|
+
export function hintsForEval(code) {
|
|
96
|
+
const hints = [];
|
|
97
|
+
for (const rule of RULES) {
|
|
98
|
+
if (hints.length >= MAX_HINTS)
|
|
99
|
+
break;
|
|
100
|
+
if (rule.none?.some(re => re.test(code)))
|
|
101
|
+
continue;
|
|
102
|
+
if (!rule.all.every(re => re.test(code)))
|
|
103
|
+
continue;
|
|
104
|
+
hints.push({ tool: rule.tool, why: rule.why });
|
|
105
|
+
}
|
|
106
|
+
return hints;
|
|
107
|
+
}
|
|
108
|
+
export function formatEvalHints(hints) {
|
|
109
|
+
if (hints.length === 0)
|
|
110
|
+
return null;
|
|
111
|
+
return hints
|
|
112
|
+
.map(h => `note: \`${h.tool}\` does this directly — it ${h.why}.`)
|
|
113
|
+
.join('\n');
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=eval-hints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval-hints.js","sourceRoot":"","sources":["../src/eval-hints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
import type { ArtifactKind } from './artifact-classes.js';
|
|
12
|
+
import type { DevOnlyTotals, DevRootCategory } from './tools/dev-artifacts.js';
|
|
13
|
+
export interface HeapBudget {
|
|
14
|
+
total: number;
|
|
15
|
+
app: number;
|
|
16
|
+
appNodes: number;
|
|
17
|
+
bundle: number;
|
|
18
|
+
code: number;
|
|
19
|
+
devOnly: number;
|
|
20
|
+
artifact: number;
|
|
21
|
+
/** Known-artifact self bytes split by family (warmup / ax / cdp-* / …). */
|
|
22
|
+
artifactByKind: Map<ArtifactKind, number>;
|
|
23
|
+
/** Dev-only totals split by root family, or null when no dev roots exist. */
|
|
24
|
+
devOnlyTotals: DevOnlyTotals | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function computeHeapBudget(snapshot: IHeapSnapshot): HeapBudget;
|
|
27
|
+
export declare function devFamilyBytes(budget: HeapBudget): Array<{
|
|
28
|
+
family: DevRootCategory;
|
|
29
|
+
nodes: number;
|
|
30
|
+
selfBytes: number;
|
|
31
|
+
}>;
|
|
32
|
+
//# sourceMappingURL=heap-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heap-budget.d.ts","sourceRoot":"","sources":["../src/heap-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAY,aAAa,EAAC,MAAM,cAAc,CAAC;AAE3D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAMxD,OAAO,KAAK,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAmB7E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,cAAc,EAAE,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC1C,6EAA6E;IAC7E,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;CACrC;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,UAAU,CA2DrE;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,GACjB,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,CAAC,CAapE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
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 { classifyArtifact } from './artifact-classes.js';
|
|
11
|
+
import { collectDevRoots, computeReachableWithoutDevRoots, summarizeDevOnly, } from './tools/dev-artifacts.js';
|
|
12
|
+
/**
|
|
13
|
+
* The one bucket split of a heap into "app" and the several distinct reasons a
|
|
14
|
+
* byte might not be the app.
|
|
15
|
+
*
|
|
16
|
+
* Extracted so `memlab_app_heap` and `memlab_artifact_budget` cannot drift: they
|
|
17
|
+
* are two views of the same partition, and two implementations of "what counts
|
|
18
|
+
* as the app" that disagree by a few percent is worse than either one alone,
|
|
19
|
+
* because the disagreement is invisible in each tool's own output.
|
|
20
|
+
*/
|
|
21
|
+
const CODE_TYPES = new Set(['code']);
|
|
22
|
+
// Bundle source text and the V8 structures that hold it. These exist because
|
|
23
|
+
// the app was *loaded*, not because it did anything.
|
|
24
|
+
const BUNDLE_NAME_RE = /ExternalStringData|InstructionStream|BytecodeArray|SharedFunctionInfo|ScopeInfo|UncompiledData|FeedbackVector|FeedbackMetadata|FeedbackCell|ClosureFeedbackCellArray|\(constant pool\)|ConstantPool/;
|
|
25
|
+
export function computeHeapBudget(snapshot) {
|
|
26
|
+
const devRoots = collectDevRoots(snapshot);
|
|
27
|
+
const reached = devRoots.byId.size > 0
|
|
28
|
+
? computeReachableWithoutDevRoots(snapshot, devRoots)
|
|
29
|
+
: null;
|
|
30
|
+
let total = 0;
|
|
31
|
+
let code = 0;
|
|
32
|
+
let bundle = 0;
|
|
33
|
+
let devOnly = 0;
|
|
34
|
+
let artifact = 0;
|
|
35
|
+
let app = 0;
|
|
36
|
+
let appNodes = 0;
|
|
37
|
+
const artifactByKind = new Map();
|
|
38
|
+
const isDevOnly = (node) => reached != null && reached[node.nodeIndex] === 0;
|
|
39
|
+
snapshot.nodes.forEach((node) => {
|
|
40
|
+
if (node.id <= 3)
|
|
41
|
+
return;
|
|
42
|
+
const size = node.self_size;
|
|
43
|
+
total += size;
|
|
44
|
+
// Precedence matters: a node is counted once, in the most specific bucket
|
|
45
|
+
// that explains why it is NOT app memory.
|
|
46
|
+
if (isDevOnly(node)) {
|
|
47
|
+
devOnly += size;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (CODE_TYPES.has(node.type)) {
|
|
51
|
+
code += size;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (BUNDLE_NAME_RE.test(node.name)) {
|
|
55
|
+
bundle += size;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const kind = classifyArtifact(node.name);
|
|
59
|
+
if (kind != null) {
|
|
60
|
+
artifact += size;
|
|
61
|
+
artifactByKind.set(kind, (artifactByKind.get(kind) ?? 0) + size);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
app += size;
|
|
65
|
+
appNodes++;
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
total,
|
|
69
|
+
app,
|
|
70
|
+
appNodes,
|
|
71
|
+
bundle,
|
|
72
|
+
code,
|
|
73
|
+
devOnly,
|
|
74
|
+
artifact,
|
|
75
|
+
artifactByKind,
|
|
76
|
+
devOnlyTotals: reached != null ? summarizeDevOnly(snapshot, devRoots, reached) : null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function devFamilyBytes(budget) {
|
|
80
|
+
const out = [];
|
|
81
|
+
const byCategory = budget.devOnlyTotals?.byCategory;
|
|
82
|
+
if (byCategory == null)
|
|
83
|
+
return out;
|
|
84
|
+
for (const [family, v] of byCategory) {
|
|
85
|
+
out.push({ family, nodes: v.nodes, selfBytes: v.selfBytes });
|
|
86
|
+
}
|
|
87
|
+
out.sort((a, b) => b.selfBytes - a.selfBytes);
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=heap-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heap-budget.js","sourceRoot":"","sources":["../src/heap-budget.ts"],"names":[],"mappings":"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"}
|