@memlab/mcp-server 2.1.0 → 2.3.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 +88 -3
- package/dist/analysis-budget.d.ts +71 -0
- package/dist/analysis-budget.d.ts.map +1 -0
- package/dist/analysis-budget.js +118 -0
- package/dist/analysis-budget.js.map +1 -0
- package/dist/guardrail.d.ts +24 -0
- package/dist/guardrail.d.ts.map +1 -0
- package/dist/guardrail.js +65 -0
- package/dist/guardrail.js.map +1 -0
- package/dist/heap-state.d.ts +46 -1
- package/dist/heap-state.d.ts.map +1 -1
- package/dist/heap-state.js +142 -10
- package/dist/heap-state.js.map +1 -1
- package/dist/index.js +36 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/array-group-by.d.ts +12 -0
- package/dist/tools/array-group-by.d.ts.map +1 -0
- package/dist/tools/array-group-by.js +191 -0
- package/dist/tools/array-group-by.js.map +1 -0
- package/dist/tools/auto-investigate.d.ts.map +1 -1
- package/dist/tools/auto-investigate.js +999 -37
- package/dist/tools/auto-investigate.js.map +1 -1
- package/dist/tools/cache-analysis.d.ts.map +1 -1
- package/dist/tools/cache-analysis.js +438 -21
- package/dist/tools/cache-analysis.js.map +1 -1
- package/dist/tools/check-health.d.ts.map +1 -1
- package/dist/tools/check-health.js +261 -1
- package/dist/tools/check-health.js.map +1 -1
- package/dist/tools/class-histogram.d.ts.map +1 -1
- package/dist/tools/class-histogram.js +142 -33
- package/dist/tools/class-histogram.js.map +1 -1
- package/dist/tools/closure-inspection.d.ts.map +1 -1
- package/dist/tools/closure-inspection.js +238 -25
- package/dist/tools/closure-inspection.js.map +1 -1
- package/dist/tools/detached-dom.d.ts.map +1 -1
- package/dist/tools/detached-dom.js +25 -3
- package/dist/tools/detached-dom.js.map +1 -1
- package/dist/tools/dev-artifacts.d.ts +33 -0
- package/dist/tools/dev-artifacts.d.ts.map +1 -0
- package/dist/tools/dev-artifacts.js +165 -0
- package/dist/tools/dev-artifacts.js.map +1 -0
- package/dist/tools/diff-snapshots.d.ts.map +1 -1
- package/dist/tools/diff-snapshots.js +113 -18
- package/dist/tools/diff-snapshots.js.map +1 -1
- package/dist/tools/duplicated-strings.d.ts.map +1 -1
- package/dist/tools/duplicated-strings.js +74 -12
- package/dist/tools/duplicated-strings.js.map +1 -1
- package/dist/tools/eval.d.ts.map +1 -1
- package/dist/tools/eval.js +189 -10
- 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 +235 -13
- package/dist/tools/event-listener-leaks.js.map +1 -1
- package/dist/tools/event-registry.d.ts +12 -0
- package/dist/tools/event-registry.d.ts.map +1 -0
- package/dist/tools/event-registry.js +183 -0
- package/dist/tools/event-registry.js.map +1 -0
- package/dist/tools/find-by-property.d.ts.map +1 -1
- package/dist/tools/find-by-property.js +113 -31
- package/dist/tools/find-by-property.js.map +1 -1
- package/dist/tools/find-by-shape.d.ts +12 -0
- package/dist/tools/find-by-shape.d.ts.map +1 -0
- package/dist/tools/find-by-shape.js +178 -0
- package/dist/tools/find-by-shape.js.map +1 -0
- package/dist/tools/find-nodes-by-class.d.ts.map +1 -1
- package/dist/tools/find-nodes-by-class.js +50 -6
- package/dist/tools/find-nodes-by-class.js.map +1 -1
- package/dist/tools/for-each.d.ts.map +1 -1
- package/dist/tools/for-each.js +1 -1
- package/dist/tools/for-each.js.map +1 -1
- package/dist/tools/get-node.d.ts.map +1 -1
- package/dist/tools/get-node.js +6 -0
- package/dist/tools/get-node.js.map +1 -1
- package/dist/tools/get-string.d.ts +12 -0
- package/dist/tools/get-string.d.ts.map +1 -0
- package/dist/tools/get-string.js +223 -0
- package/dist/tools/get-string.js.map +1 -0
- package/dist/tools/get-value.d.ts +12 -0
- package/dist/tools/get-value.d.ts.map +1 -0
- package/dist/tools/get-value.js +152 -0
- package/dist/tools/get-value.js.map +1 -0
- package/dist/tools/global-variables.d.ts.map +1 -1
- package/dist/tools/global-variables.js +54 -34
- package/dist/tools/global-variables.js.map +1 -1
- package/dist/tools/growth-signals.d.ts +12 -0
- package/dist/tools/growth-signals.d.ts.map +1 -0
- package/dist/tools/growth-signals.js +211 -0
- package/dist/tools/growth-signals.js.map +1 -0
- package/dist/tools/intern-opportunities.d.ts +12 -0
- package/dist/tools/intern-opportunities.d.ts.map +1 -0
- package/dist/tools/intern-opportunities.js +289 -0
- package/dist/tools/intern-opportunities.js.map +1 -0
- package/dist/tools/load-snapshot.d.ts +10 -0
- package/dist/tools/load-snapshot.d.ts.map +1 -1
- package/dist/tools/load-snapshot.js +152 -15
- package/dist/tools/load-snapshot.js.map +1 -1
- package/dist/tools/object-cost-breakdown.d.ts +12 -0
- package/dist/tools/object-cost-breakdown.d.ts.map +1 -0
- package/dist/tools/object-cost-breakdown.js +439 -0
- package/dist/tools/object-cost-breakdown.js.map +1 -0
- package/dist/tools/object-shape.d.ts.map +1 -1
- package/dist/tools/object-shape.js +95 -2
- package/dist/tools/object-shape.js.map +1 -1
- package/dist/tools/pinch-points.d.ts.map +1 -1
- package/dist/tools/pinch-points.js +9 -9
- package/dist/tools/pinch-points.js.map +1 -1
- package/dist/tools/property-distribution.d.ts +12 -0
- package/dist/tools/property-distribution.d.ts.map +1 -0
- package/dist/tools/property-distribution.js +168 -0
- package/dist/tools/property-distribution.js.map +1 -0
- package/dist/tools/quick-diagnosis.d.ts.map +1 -1
- package/dist/tools/quick-diagnosis.js +56 -19
- package/dist/tools/quick-diagnosis.js.map +1 -1
- package/dist/tools/retainer-summary.d.ts.map +1 -1
- package/dist/tools/retainer-summary.js +90 -5
- 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 +74 -22
- package/dist/tools/retainer-trace.js.map +1 -1
- package/dist/tools/search-nodes.d.ts.map +1 -1
- package/dist/tools/search-nodes.js +10 -3
- package/dist/tools/search-nodes.js.map +1 -1
- package/dist/tools/search-strings.d.ts +12 -0
- package/dist/tools/search-strings.d.ts.map +1 -0
- package/dist/tools/search-strings.js +182 -0
- package/dist/tools/search-strings.js.map +1 -0
- package/dist/tools/sequence-analysis.d.ts +12 -0
- package/dist/tools/sequence-analysis.d.ts.map +1 -0
- package/dist/tools/sequence-analysis.js +234 -0
- package/dist/tools/sequence-analysis.js.map +1 -0
- package/dist/tools/server-status.d.ts +12 -0
- package/dist/tools/server-status.d.ts.map +1 -0
- package/dist/tools/server-status.js +41 -0
- package/dist/tools/server-status.js.map +1 -0
- package/dist/tools/shape-histogram.d.ts.map +1 -1
- package/dist/tools/shape-histogram.js +61 -16
- 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 +16 -11
- package/dist/tools/sliced-strings.js.map +1 -1
- package/dist/tools/snapshot-summary.d.ts.map +1 -1
- package/dist/tools/snapshot-summary.js +55 -13
- package/dist/tools/snapshot-summary.js.map +1 -1
- package/dist/tools/snapshots.d.ts +12 -0
- package/dist/tools/snapshots.d.ts.map +1 -0
- package/dist/tools/snapshots.js +93 -0
- package/dist/tools/snapshots.js.map +1 -0
- package/dist/tools/stale-collections.d.ts.map +1 -1
- package/dist/tools/stale-collections.js +248 -7
- package/dist/tools/stale-collections.js.map +1 -1
- package/dist/tools/trace-dominators.d.ts.map +1 -1
- package/dist/tools/trace-dominators.js +28 -1
- package/dist/tools/trace-dominators.js.map +1 -1
- package/dist/tools/weakmap-entries.d.ts +12 -0
- package/dist/tools/weakmap-entries.d.ts.map +1 -0
- package/dist/tools/weakmap-entries.js +156 -0
- package/dist/tools/weakmap-entries.js.map +1 -0
- package/dist/utils.d.ts +81 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +303 -35
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import memlabCore from '@memlab/core';
|
|
11
11
|
const { utils, NumericSet } = memlabCore;
|
|
12
|
+
import { z } from 'zod';
|
|
12
13
|
import { getSnapshot, getFilePath } from '../heap-state.js';
|
|
13
|
-
import { formatBytes, formatNumber, markdownTable, errorResult, toolResult, } from '../utils.js';
|
|
14
|
+
import { formatBytes, formatNumber, markdownTable, detectAppName, errorResult, toolResult, } from '../utils.js';
|
|
14
15
|
export function registerSnapshotSummary(server) {
|
|
15
|
-
server.tool('memlab_snapshot_summary', 'Get an overview of the loaded heap snapshot: total nodes, edges, size, and
|
|
16
|
+
server.tool('memlab_snapshot_summary', 'Get an overview of the loaded heap snapshot: total nodes, edges, size, and per-node-type breakdown with count and self size. ' +
|
|
17
|
+
'By DEFAULT retained size per type is reported as a cheap RANGE (lower bound = Σ self size, upper bound = Σ retained size) computed in a single O(N) pass — the exact dominator-deduped retained size needs a per-type dominator walk that can stall/OOM the server on very large heaps. Pass `exact_retained_size:true` ONLY when the user explicitly asks for the exact figure. ' +
|
|
18
|
+
'Includes anomaly detection for high-count classes. Follow up with memlab_class_histogram for a per-class breakdown, or memlab_duplicated_strings to find string duplication.', {
|
|
19
|
+
exact_retained_size: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.optional()
|
|
22
|
+
.default(false)
|
|
23
|
+
.describe('Compute the EXACT dominator-deduped retained size per node type via aggregateDominatorMetrics. DEFAULT false: report a range instead (lower = Σ self size, upper = Σ raw retained size), which is fast and safe on huge heaps. Set true ONLY on explicit user request — the exact walk can be very slow or time out on snapshots with millions of nodes and deep dominator chains.'),
|
|
24
|
+
}, async ({ exact_retained_size }) => {
|
|
16
25
|
try {
|
|
17
26
|
const snapshot = getSnapshot();
|
|
18
27
|
let nodeCount = 0;
|
|
@@ -24,45 +33,78 @@ export function registerSnapshotSummary(server) {
|
|
|
24
33
|
totalSelfSize += node.self_size;
|
|
25
34
|
let stats = typeStats.get(node.type);
|
|
26
35
|
if (!stats) {
|
|
27
|
-
stats = {
|
|
36
|
+
stats = {
|
|
37
|
+
count: 0,
|
|
38
|
+
self_size: 0,
|
|
39
|
+
raw_retained: 0,
|
|
40
|
+
// Only collect per-node ids for the exact dominator walk, which
|
|
41
|
+
// is opt-in — in the default range mode they would waste memory.
|
|
42
|
+
node_ids: exact_retained_size ? new NumericSet() : null,
|
|
43
|
+
};
|
|
28
44
|
typeStats.set(node.type, stats);
|
|
29
45
|
}
|
|
30
46
|
stats.count++;
|
|
31
47
|
stats.self_size += node.self_size;
|
|
32
|
-
stats.
|
|
48
|
+
stats.raw_retained += node.retainedSize;
|
|
49
|
+
if (stats.node_ids)
|
|
50
|
+
stats.node_ids.add(node.id);
|
|
33
51
|
});
|
|
34
52
|
snapshot.edges.forEach(() => {
|
|
35
53
|
edgeCount++;
|
|
36
54
|
});
|
|
37
|
-
//
|
|
38
|
-
// aggregation.
|
|
39
|
-
//
|
|
40
|
-
//
|
|
55
|
+
// Retained size per type. By DEFAULT we do NOT run the exact
|
|
56
|
+
// dominator-aware aggregation (utils.aggregateDominatorMetrics): on
|
|
57
|
+
// large heaps with deep dominator chains it is the operation that
|
|
58
|
+
// stalls/OOMs the server. Instead we report a cheap range computed in
|
|
59
|
+
// the O(N) pass above:
|
|
60
|
+
// - lower bound = Σ self size (the objects' own bytes; never
|
|
61
|
+
// double-counts, but excludes everything they retain)
|
|
62
|
+
// - upper bound = Σ retained size (raw; double-counts memory shared
|
|
63
|
+
// between instances on the dominator tree)
|
|
64
|
+
// The true dominator-deduped value lies in [lower, upper]. Pass
|
|
65
|
+
// exact_retained_size:true to compute the exact figure anyway.
|
|
41
66
|
const breakdown = [];
|
|
42
67
|
for (const [type, stats] of typeStats) {
|
|
43
|
-
|
|
68
|
+
let exact = null;
|
|
69
|
+
if (exact_retained_size && stats.node_ids) {
|
|
70
|
+
exact = utils.aggregateDominatorMetrics(stats.node_ids, snapshot, () => true, (node) => node.retainedSize);
|
|
71
|
+
}
|
|
44
72
|
breakdown.push({
|
|
45
73
|
type,
|
|
46
74
|
count: stats.count,
|
|
47
75
|
self_size: stats.self_size,
|
|
48
|
-
|
|
76
|
+
raw_retained: stats.raw_retained,
|
|
77
|
+
exact,
|
|
49
78
|
});
|
|
50
79
|
}
|
|
51
|
-
breakdown.sort((a, b) =>
|
|
80
|
+
breakdown.sort((a, b) => exact_retained_size
|
|
81
|
+
? (b.exact ?? 0) - (a.exact ?? 0)
|
|
82
|
+
: b.raw_retained - a.raw_retained);
|
|
83
|
+
const appName = detectAppName(snapshot);
|
|
52
84
|
const lines = [
|
|
53
85
|
`**File:** ${getFilePath()}`,
|
|
86
|
+
...(appName
|
|
87
|
+
? [`**Detected app:** \`${appName}\` (from bundle paths)`]
|
|
88
|
+
: []),
|
|
54
89
|
`**Nodes:** ${formatNumber(nodeCount)} | **Edges:** ${formatNumber(edgeCount)} | **Total Self Size:** ${formatBytes(totalSelfSize)}`,
|
|
55
90
|
'',
|
|
56
91
|
];
|
|
57
|
-
const headers =
|
|
92
|
+
const headers = exact_retained_size
|
|
93
|
+
? ['Type', 'Count', 'Self Size', 'Retained Size']
|
|
94
|
+
: ['Type', 'Count', 'Self (lower)', 'Retained ≤ (upper)'];
|
|
58
95
|
const rightCols = new Set([1, 2, 3]);
|
|
59
96
|
const rows = breakdown.map(b => [
|
|
60
97
|
b.type,
|
|
61
98
|
formatNumber(b.count),
|
|
62
99
|
formatBytes(b.self_size),
|
|
63
|
-
|
|
100
|
+
exact_retained_size
|
|
101
|
+
? formatBytes(b.exact ?? 0)
|
|
102
|
+
: `≤ ${formatBytes(b.raw_retained)}`,
|
|
64
103
|
]);
|
|
65
104
|
lines.push(markdownTable(headers, rows, rightCols));
|
|
105
|
+
if (!exact_retained_size) {
|
|
106
|
+
lines.push('', "_Retained size is shown as a **range** to avoid an expensive dominator walk. The true dominator-deduped retained size of each type lies between the **lower bound** (`Self` = the objects' own bytes) and the **upper bound** (`Retained ≤` = Σ of each instance's retained size, which double-counts memory shared between instances on the dominator tree). Computing the exact value needs a per-type dominator walk — slow and, on large heaps with deep PromiseReaction/Context chains, prone to stalling or timing out. Pass `exact_retained_size:true` to compute it anyway._");
|
|
107
|
+
}
|
|
66
108
|
// Anomaly detection: find classes with unusually high instance counts
|
|
67
109
|
const classMap = new Map();
|
|
68
110
|
snapshot.nodes.forEach(node => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-summary.js","sourceRoot":"","sources":["../../src/tools/snapshot-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,UAAU,MAAM,cAAc,CAAC;AAEtC,MAAM,EAAC,KAAK,EAAE,UAAU,EAAC,GAAG,UAAU,CAAC;AACvC,OAAO,EAAC,WAAW,EAAE,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,WAAW,
|
|
1
|
+
{"version":3,"file":"snapshot-summary.js","sourceRoot":"","sources":["../../src/tools/snapshot-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
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 { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
export declare function registerSnapshots(server: McpServer): void;
|
|
12
|
+
//# sourceMappingURL=snapshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../src/tools/snapshots.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAYvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyGzD"}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 { z } from 'zod';
|
|
11
|
+
import { listSnapshots, getCurrentHandle, setCurrentSnapshot, removeSnapshot, setSessionConfig, getSessionConfig, } from '../heap-state.js';
|
|
12
|
+
import { formatBytes, formatNumber, errorResult, textResult } from '../utils.js';
|
|
13
|
+
export function registerSnapshots(server) {
|
|
14
|
+
server.tool('memlab_snapshots', 'Manage the multi-snapshot session: list resident snapshots, switch the active one, or unload one to free memory. Also toggles session-level output controls (quiet header, suppress suggestions) to trim repeated boilerplate tokens. Load several snapshots with memlab_load_snapshot({keep_previous:true}) then switch between them by handle. Node ids are only valid within the snapshot they came from.', {
|
|
15
|
+
action: z
|
|
16
|
+
.enum(['list', 'switch', 'unload'])
|
|
17
|
+
.optional()
|
|
18
|
+
.default('list')
|
|
19
|
+
.describe('"list" (default) shows resident snapshots; "switch" makes the given handle active; "unload" removes the given handle to free memory.'),
|
|
20
|
+
handle: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Snapshot handle to switch to or unload (see action).'),
|
|
24
|
+
quiet: z
|
|
25
|
+
.boolean()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('When set, toggles the session "quiet header" mode: the "> Snapshot: …" header prints once per snapshot instead of on every tool result.'),
|
|
28
|
+
suppress_suggestions: z
|
|
29
|
+
.boolean()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('When set, toggles whether tools omit their "Suggested next steps" trailers.'),
|
|
32
|
+
}, async ({ action, handle, quiet, suppress_suggestions }) => {
|
|
33
|
+
try {
|
|
34
|
+
const configChanges = [];
|
|
35
|
+
if (quiet != null || suppress_suggestions != null) {
|
|
36
|
+
setSessionConfig({
|
|
37
|
+
...(quiet != null ? { quietHeader: quiet } : {}),
|
|
38
|
+
...(suppress_suggestions != null
|
|
39
|
+
? { suppressSuggestions: suppress_suggestions }
|
|
40
|
+
: {}),
|
|
41
|
+
});
|
|
42
|
+
const cfg = getSessionConfig();
|
|
43
|
+
configChanges.push(`Output config: quietHeader=${cfg.quietHeader}, suppressSuggestions=${cfg.suppressSuggestions}`);
|
|
44
|
+
}
|
|
45
|
+
if (action === 'switch') {
|
|
46
|
+
if (!handle) {
|
|
47
|
+
return errorResult('action "switch" requires a handle.');
|
|
48
|
+
}
|
|
49
|
+
if (!setCurrentSnapshot(handle)) {
|
|
50
|
+
return errorResult(`No resident snapshot with handle "${handle}". Use action:"list" to see handles.`);
|
|
51
|
+
}
|
|
52
|
+
return textResult([`Active snapshot is now "${handle}".`, ...configChanges].join('\n'));
|
|
53
|
+
}
|
|
54
|
+
if (action === 'unload') {
|
|
55
|
+
if (!handle) {
|
|
56
|
+
return errorResult('action "unload" requires a handle.');
|
|
57
|
+
}
|
|
58
|
+
if (!removeSnapshot(handle)) {
|
|
59
|
+
return errorResult(`No resident snapshot with handle "${handle}".`);
|
|
60
|
+
}
|
|
61
|
+
const cur = getCurrentHandle();
|
|
62
|
+
return textResult([
|
|
63
|
+
`Unloaded snapshot "${handle}".`,
|
|
64
|
+
cur
|
|
65
|
+
? `Active snapshot is now "${cur}".`
|
|
66
|
+
: 'No snapshots remain loaded.',
|
|
67
|
+
...configChanges,
|
|
68
|
+
].join('\n'));
|
|
69
|
+
}
|
|
70
|
+
// list
|
|
71
|
+
const all = listSnapshots();
|
|
72
|
+
const cur = getCurrentHandle();
|
|
73
|
+
if (all.length === 0) {
|
|
74
|
+
return textResult([
|
|
75
|
+
'No snapshots loaded. Use memlab_load_snapshot first.',
|
|
76
|
+
...configChanges,
|
|
77
|
+
].join('\n'));
|
|
78
|
+
}
|
|
79
|
+
const lines = [`Resident snapshots (${all.length}):`];
|
|
80
|
+
for (const m of all) {
|
|
81
|
+
lines.push(`${m.handle === cur ? '→' : ' '} ${m.handle} — ${m.fileName}, ` +
|
|
82
|
+
`${formatBytes(m.totalSize)}, ${formatNumber(m.nodeCount)} nodes (${m.env})`);
|
|
83
|
+
}
|
|
84
|
+
if (configChanges.length > 0)
|
|
85
|
+
lines.push('', ...configChanges);
|
|
86
|
+
return textResult(lines.join('\n'));
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return errorResult(err);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=snapshots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshots.js","sourceRoot":"","sources":["../../src/tools/snapshots.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stale-collections.d.ts","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"stale-collections.d.ts","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAoKvE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiQhE"}
|
|
@@ -34,8 +34,123 @@ function getCollectionChildren(node) {
|
|
|
34
34
|
// For Array and others, use direct references
|
|
35
35
|
return node.references;
|
|
36
36
|
}
|
|
37
|
+
function isOrphanedEntry(node) {
|
|
38
|
+
if (node.id <= 3)
|
|
39
|
+
return false;
|
|
40
|
+
if (node.type !== 'object')
|
|
41
|
+
return false;
|
|
42
|
+
return node.numOfReferrers <= 1;
|
|
43
|
+
}
|
|
44
|
+
const LISTENER_CALLBACK_PROPS = new Set([
|
|
45
|
+
'callback',
|
|
46
|
+
'fn',
|
|
47
|
+
'handler',
|
|
48
|
+
'listener',
|
|
49
|
+
]);
|
|
50
|
+
const LISTENER_CONTEXT_PROPS = new Set([
|
|
51
|
+
'context',
|
|
52
|
+
'ctx',
|
|
53
|
+
'this',
|
|
54
|
+
'target',
|
|
55
|
+
'scope',
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* One pass over the heap to collect the ids of objects that look like a listener
|
|
59
|
+
* record — i.e. they carry BOTH a callback-ish and a context-ish property
|
|
60
|
+
* (`{callback, context}`, `{fn, ctx}`, …). Precomputing this once turns the
|
|
61
|
+
* `listener_orphaned` check from O(entries × referrers × referrer-edges) — a
|
|
62
|
+
* per-entry re-walk of every referrer's outgoing edges — into an O(1) set lookup
|
|
63
|
+
* per referrer, which is what made the unbounded scan wedge on large Node heaps.
|
|
64
|
+
*/
|
|
65
|
+
function buildListenerObjectIds(snapshot, deadline) {
|
|
66
|
+
const ids = new Set();
|
|
67
|
+
let timedOut = false;
|
|
68
|
+
let seen = 0;
|
|
69
|
+
snapshot.nodes.forEach(node => {
|
|
70
|
+
if (timedOut)
|
|
71
|
+
return;
|
|
72
|
+
// This precompute is a full O(N) pass; keep it inside the same wall-clock
|
|
73
|
+
// budget as the scan so timeout_ms bounds the whole operation.
|
|
74
|
+
if ((seen++ & 0x7ff) === 0 && Date.now() > deadline) {
|
|
75
|
+
timedOut = true;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (node.type !== 'object' || node.id <= 3)
|
|
79
|
+
return;
|
|
80
|
+
let hasCallback = false;
|
|
81
|
+
let hasContext = false;
|
|
82
|
+
for (const edge of node.references) {
|
|
83
|
+
if (edge.type !== 'property')
|
|
84
|
+
continue;
|
|
85
|
+
const pName = String(edge.name_or_index);
|
|
86
|
+
if (LISTENER_CALLBACK_PROPS.has(pName))
|
|
87
|
+
hasCallback = true;
|
|
88
|
+
else if (LISTENER_CONTEXT_PROPS.has(pName))
|
|
89
|
+
hasContext = true;
|
|
90
|
+
if (hasCallback && hasContext) {
|
|
91
|
+
ids.add(node.id);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return { ids, timedOut };
|
|
97
|
+
}
|
|
98
|
+
function isListenerOrphaned(node, ownershipEdges, listenerObjectIds) {
|
|
99
|
+
if (node.id <= 3)
|
|
100
|
+
return false;
|
|
101
|
+
if (node.type !== 'object')
|
|
102
|
+
return false;
|
|
103
|
+
// Single pass over the entry's (typically few) referrers: an ownership edge
|
|
104
|
+
// disqualifies it; a referrer that is a known listener record qualifies it.
|
|
105
|
+
let hasListenerRef = false;
|
|
106
|
+
for (const edge of node.referrers) {
|
|
107
|
+
if (ownershipEdges.has(String(edge.name_or_index))) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (listenerObjectIds.has(edge.fromNode.id)) {
|
|
111
|
+
hasListenerRef = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return hasListenerRef;
|
|
115
|
+
}
|
|
116
|
+
function hasTerminalStatus(node) {
|
|
117
|
+
if (node.id <= 3)
|
|
118
|
+
return false;
|
|
119
|
+
if (node.type !== 'object')
|
|
120
|
+
return false;
|
|
121
|
+
const TERMINAL_VALUES = new Set([
|
|
122
|
+
'PENDING',
|
|
123
|
+
'UPLOAD_PENDING',
|
|
124
|
+
'UPLOADING',
|
|
125
|
+
'FAILED',
|
|
126
|
+
'ERROR',
|
|
127
|
+
'TIMED_OUT',
|
|
128
|
+
'CANCELLED',
|
|
129
|
+
'EXPIRED',
|
|
130
|
+
'ABORTED',
|
|
131
|
+
]);
|
|
132
|
+
for (const edge of node.references) {
|
|
133
|
+
const eName = String(edge.name_or_index);
|
|
134
|
+
if (eName === 'status' ||
|
|
135
|
+
eName === 'state' ||
|
|
136
|
+
eName === '_status' ||
|
|
137
|
+
eName === '_state') {
|
|
138
|
+
const target = edge.toNode;
|
|
139
|
+
if (target.isString) {
|
|
140
|
+
const strNode = target.toStringNode();
|
|
141
|
+
if (strNode && TERMINAL_VALUES.has(strNode.stringValue)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
37
149
|
export function registerStaleCollections(server) {
|
|
38
|
-
server.tool('memlab_stale_collections', 'Find Map, Set, and Array collections holding references
|
|
150
|
+
server.tool('memlab_stale_collections', 'Find Map, Set, and Array collections holding stale references. Detects: (1) detached DOM / ' +
|
|
151
|
+
'unmounted Fiber nodes, (2) entries with a status/state property stuck at terminal values ' +
|
|
152
|
+
'(PENDING, FAILED, TIMED_OUT, etc.), and (3) orphaned entries with no referrers outside ' +
|
|
153
|
+
'the collection. Use detect_modes to control which heuristics are applied.', {
|
|
39
154
|
limit: z
|
|
40
155
|
.number()
|
|
41
156
|
.optional()
|
|
@@ -51,22 +166,132 @@ export function registerStaleCollections(server) {
|
|
|
51
166
|
.optional()
|
|
52
167
|
.default(0)
|
|
53
168
|
.describe('Minimum total stale retained size in bytes to include (default 0). Use e.g., 10240 for 10 KB to filter noise.'),
|
|
54
|
-
|
|
169
|
+
detect_modes: z
|
|
170
|
+
.array(z.enum([
|
|
171
|
+
'detached',
|
|
172
|
+
'terminal_status',
|
|
173
|
+
'orphaned',
|
|
174
|
+
'listener_orphaned',
|
|
175
|
+
]))
|
|
176
|
+
.optional()
|
|
177
|
+
.default(['detached', 'terminal_status'])
|
|
178
|
+
.describe('Which stale detection heuristics to apply. "detached" = detached DOM / unmounted Fiber, ' +
|
|
179
|
+
'"terminal_status" = entries with status stuck at terminal values, ' +
|
|
180
|
+
'"orphaned" = entries with no referrers outside the collection (can be slow), ' +
|
|
181
|
+
'"listener_orphaned" = objects only kept alive by event listener registrations ' +
|
|
182
|
+
'(no ownership references like "msgs", "items", "children" — the classic listener-based ' +
|
|
183
|
+
'memory leak pattern). Default: detached + terminal_status.'),
|
|
184
|
+
ownership_edges: z
|
|
185
|
+
.array(z.string())
|
|
186
|
+
.optional()
|
|
187
|
+
.default([
|
|
188
|
+
'msgs',
|
|
189
|
+
'msgChunks',
|
|
190
|
+
'items',
|
|
191
|
+
'children',
|
|
192
|
+
'models',
|
|
193
|
+
'_models',
|
|
194
|
+
'entries',
|
|
195
|
+
'data',
|
|
196
|
+
'elements',
|
|
197
|
+
])
|
|
198
|
+
.describe('Property names that constitute "ownership" references for the listener_orphaned ' +
|
|
199
|
+
'detection mode. Objects that lack any of these incoming edges are considered orphaned. ' +
|
|
200
|
+
"Customize for your app's collection naming conventions."),
|
|
201
|
+
timeout_ms: z
|
|
202
|
+
.number()
|
|
203
|
+
.optional()
|
|
204
|
+
.default(45000)
|
|
205
|
+
.describe('Wall-clock budget for the scan (default 45000). On very large heaps the per-entry ' +
|
|
206
|
+
'checks (esp. orphaned/listener_orphaned) can be expensive; when the budget is hit the ' +
|
|
207
|
+
'tool returns partial results with a note instead of hanging.'),
|
|
208
|
+
max_children_per_collection: z
|
|
209
|
+
.number()
|
|
210
|
+
.optional()
|
|
211
|
+
.default(200000)
|
|
212
|
+
.describe('Cap on how many entries are examined per collection (default 200000). Collections larger ' +
|
|
213
|
+
'than this are partially scanned (noted in output) so one giant collection cannot dominate runtime.'),
|
|
214
|
+
}, async ({ limit, min_stale_count, min_stale_retained_size, detect_modes, ownership_edges, timeout_ms, max_children_per_collection, }) => {
|
|
55
215
|
try {
|
|
56
216
|
const snapshot = getSnapshot();
|
|
57
217
|
const results = [];
|
|
218
|
+
const detectDetached = detect_modes.includes('detached');
|
|
219
|
+
const detectTerminal = detect_modes.includes('terminal_status');
|
|
220
|
+
const detectOrphaned = detect_modes.includes('orphaned');
|
|
221
|
+
const detectListenerOrphaned = detect_modes.includes('listener_orphaned');
|
|
222
|
+
const ownershipEdgeSet = new Set(ownership_edges);
|
|
223
|
+
// Wall-clock budget + per-collection cap so a huge heap/collection
|
|
224
|
+
// returns partial results instead of wedging.
|
|
225
|
+
const deadline = Date.now() + timeout_ms;
|
|
226
|
+
let timedOut = false;
|
|
227
|
+
let cappedCollections = 0;
|
|
228
|
+
let scanned = 0;
|
|
229
|
+
// Precompute listener-record ids once (single pass) so the
|
|
230
|
+
// listener_orphaned check is an O(1) lookup per referrer instead of a
|
|
231
|
+
// per-entry re-walk of every referrer's edges. This pass is itself a
|
|
232
|
+
// full O(N) traversal, so it shares the same deadline as the scan —
|
|
233
|
+
// timeout_ms bounds the whole operation, precompute included.
|
|
234
|
+
let listenerObjectIds = new Set();
|
|
235
|
+
if (detectListenerOrphaned) {
|
|
236
|
+
const built = buildListenerObjectIds(snapshot, deadline);
|
|
237
|
+
listenerObjectIds = built.ids;
|
|
238
|
+
if (built.timedOut)
|
|
239
|
+
timedOut = true;
|
|
240
|
+
}
|
|
58
241
|
snapshot.nodes.forEach(node => {
|
|
242
|
+
if (timedOut)
|
|
243
|
+
return;
|
|
59
244
|
if (!isCollectionNode(node))
|
|
60
245
|
return;
|
|
61
246
|
const children = getCollectionChildren(node);
|
|
62
247
|
let staleCount = 0;
|
|
63
248
|
let staleRetainedSize = 0;
|
|
64
249
|
let totalChildren = 0;
|
|
250
|
+
let reason = '';
|
|
65
251
|
for (const edge of children) {
|
|
252
|
+
// Bounded: check the deadline every ~2k entries; cap per collection.
|
|
253
|
+
// The interval is kept small so a heavy per-entry cost (e.g.
|
|
254
|
+
// terminal_status walking each entry's edges) can't blow well past
|
|
255
|
+
// the budget between checks.
|
|
256
|
+
if ((scanned & 0x7ff) === 0 && Date.now() > deadline) {
|
|
257
|
+
timedOut = true;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
scanned++;
|
|
261
|
+
if (totalChildren >= max_children_per_collection) {
|
|
262
|
+
cappedCollections++;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
66
265
|
totalChildren++;
|
|
67
|
-
|
|
266
|
+
const child = edge.toNode;
|
|
267
|
+
if (child.id <= 3)
|
|
268
|
+
continue;
|
|
269
|
+
let isStale = false;
|
|
270
|
+
if (detectDetached && isStaleNode(child)) {
|
|
271
|
+
isStale = true;
|
|
272
|
+
if (!reason)
|
|
273
|
+
reason = 'detached DOM/Fiber';
|
|
274
|
+
}
|
|
275
|
+
if (!isStale && detectTerminal && hasTerminalStatus(child)) {
|
|
276
|
+
isStale = true;
|
|
277
|
+
if (!reason)
|
|
278
|
+
reason = 'terminal status';
|
|
279
|
+
}
|
|
280
|
+
if (!isStale && detectOrphaned && isOrphanedEntry(child)) {
|
|
281
|
+
isStale = true;
|
|
282
|
+
if (!reason)
|
|
283
|
+
reason = 'orphaned entries';
|
|
284
|
+
}
|
|
285
|
+
if (!isStale &&
|
|
286
|
+
detectListenerOrphaned &&
|
|
287
|
+
isListenerOrphaned(child, ownershipEdgeSet, listenerObjectIds)) {
|
|
288
|
+
isStale = true;
|
|
289
|
+
if (!reason)
|
|
290
|
+
reason = 'listener-only retention';
|
|
291
|
+
}
|
|
292
|
+
if (isStale) {
|
|
68
293
|
staleCount++;
|
|
69
|
-
staleRetainedSize +=
|
|
294
|
+
staleRetainedSize += child.retainedSize;
|
|
70
295
|
}
|
|
71
296
|
}
|
|
72
297
|
if (staleCount >= min_stale_count &&
|
|
@@ -76,28 +301,43 @@ export function registerStaleCollections(server) {
|
|
|
76
301
|
stale_item_count: staleCount,
|
|
77
302
|
stale_retained_size: staleRetainedSize,
|
|
78
303
|
total_children: totalChildren,
|
|
304
|
+
stale_reason: reason,
|
|
79
305
|
});
|
|
80
306
|
}
|
|
81
307
|
});
|
|
82
308
|
// Sort by stale retained size descending
|
|
83
309
|
results.sort((a, b) => b.stale_retained_size - a.stale_retained_size);
|
|
84
310
|
const topResults = results.slice(0, limit);
|
|
311
|
+
const scanNotes = [];
|
|
312
|
+
if (timedOut) {
|
|
313
|
+
const where = scanned === 0
|
|
314
|
+
? 'while precomputing listener records (before scanning collections)'
|
|
315
|
+
: `after ${formatNumber(scanned)} entries`;
|
|
316
|
+
scanNotes.push(`⚠ Scan hit the ${formatNumber(timeout_ms)}ms time budget ${where} — results are PARTIAL. Re-run with a higher \`timeout_ms\`, narrow \`detect_modes\`, or raise \`min_stale_count\` to focus.`);
|
|
317
|
+
}
|
|
318
|
+
if (cappedCollections > 0) {
|
|
319
|
+
scanNotes.push(`⚠ ${formatNumber(cappedCollections)} collection(s) exceeded \`max_children_per_collection\` (${formatNumber(max_children_per_collection)}) and were only partially scanned.`);
|
|
320
|
+
}
|
|
321
|
+
const scanNote = scanNotes.length > 0 ? scanNotes.join('\n') + '\n\n' : '';
|
|
85
322
|
if (topResults.length === 0) {
|
|
86
323
|
const env = getSnapshotEnv();
|
|
87
324
|
if (env === 'node') {
|
|
88
|
-
return toolResult(
|
|
325
|
+
return toolResult(scanNote +
|
|
326
|
+
'No stale collections found. This is a Node.js snapshot — detached DOM detection is not applicable.\n\n' +
|
|
89
327
|
'For Node.js memory investigation, try:\n' +
|
|
90
328
|
'- `memlab_largest_objects` — find objects consuming the most memory\n' +
|
|
91
329
|
'- `memlab_class_histogram` — per-class instance counts and sizes\n' +
|
|
92
330
|
'- `memlab_sliced_strings` — find sliced strings keeping large parents alive');
|
|
93
331
|
}
|
|
94
|
-
return toolResult('No stale collections found.');
|
|
332
|
+
return toolResult(scanNote + 'No stale collections found.');
|
|
95
333
|
}
|
|
334
|
+
const hasMultipleReasons = new Set(topResults.map(r => r.stale_reason)).size > 1;
|
|
96
335
|
const headers = [
|
|
97
336
|
'Collection ID',
|
|
98
337
|
'Name',
|
|
99
338
|
'Stale / Total',
|
|
100
339
|
'Stale Retained',
|
|
340
|
+
...(hasMultipleReasons ? ['Reason'] : []),
|
|
101
341
|
];
|
|
102
342
|
const rightCols = new Set([2, 3]);
|
|
103
343
|
const rows = topResults.map(r => [
|
|
@@ -105,8 +345,9 @@ export function registerStaleCollections(server) {
|
|
|
105
345
|
r.collection.name,
|
|
106
346
|
`${formatNumber(r.stale_item_count)} / ${formatNumber(r.total_children)}`,
|
|
107
347
|
formatBytes(r.stale_retained_size),
|
|
348
|
+
...(hasMultipleReasons ? [r.stale_reason] : []),
|
|
108
349
|
]);
|
|
109
|
-
const output =
|
|
350
|
+
const output = `${scanNote}Stale collections (${topResults.length} found)\n\n${markdownTable(headers, rows, rightCols)}`;
|
|
110
351
|
return toolResult(output +
|
|
111
352
|
'\n\n---\n\n' +
|
|
112
353
|
'**Suggested action:** Collections holding detached/stale references prevent garbage collection. ' +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stale-collections.js","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,WAAW,EAAE,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,WAAW,
|
|
1
|
+
{"version":3,"file":"stale-collections.js","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-dominators.d.ts","sourceRoot":"","sources":["../../src/tools/trace-dominators.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAqGvE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"trace-dominators.d.ts","sourceRoot":"","sources":["../../src/tools/trace-dominators.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAqGvE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4P/D"}
|