memorydetective 1.9.0 → 1.11.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/CHANGELOG.md +47 -1
- package/README.md +15 -11
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/referenceTree.d.ts +46 -8
- package/dist/parsers/referenceTree.js +194 -11
- package/dist/parsers/referenceTree.js.map +1 -1
- package/dist/runtime/responseFormatter.d.ts +24 -1
- package/dist/runtime/responseFormatter.js +109 -2
- package/dist/runtime/responseFormatter.js.map +1 -1
- package/dist/tools/analyzeAbandonedMemory.d.ts +26 -4
- package/dist/tools/analyzeAbandonedMemory.js +45 -7
- package/dist/tools/analyzeAbandonedMemory.js.map +1 -1
- package/dist/tools/analyzeAllocations.d.ts +3 -3
- package/dist/tools/analyzeAnimationHitches.d.ts +3 -3
- package/dist/tools/analyzeAppLaunch.d.ts +3 -3
- package/dist/tools/analyzeHangs.d.ts +3 -3
- package/dist/tools/analyzeMemgraph.d.ts +22 -3
- package/dist/tools/analyzeMemgraph.js +18 -4
- package/dist/tools/analyzeMemgraph.js.map +1 -1
- package/dist/tools/analyzeTimeProfile.d.ts +3 -3
- package/dist/tools/cleanupTraces.d.ts +4 -4
- package/dist/tools/diffMemgraphs.d.ts +59 -6
- package/dist/tools/diffMemgraphs.js +99 -2
- package/dist/tools/diffMemgraphs.js.map +1 -1
- package/dist/tools/inspectTrace.d.ts +72 -0
- package/dist/tools/inspectTrace.js +181 -0
- package/dist/tools/inspectTrace.js.map +1 -0
- package/package.json +1 -1
|
@@ -70,28 +70,211 @@ export function parseSizeBytes(raw) {
|
|
|
70
70
|
/**
|
|
71
71
|
* Pure: extract the class name from a leaks reference-tree line value.
|
|
72
72
|
*
|
|
73
|
-
* - "AVPlayerItem"
|
|
74
|
-
* - "_playerItem --> AVPlayerItemInternal"
|
|
75
|
-
* - "__strong _object --> NSKeyValueObservance"
|
|
76
|
-
* - ""
|
|
73
|
+
* - "AVPlayerItem" -> "AVPlayerItem"
|
|
74
|
+
* - "_playerItem --> AVPlayerItemInternal" -> "AVPlayerItemInternal"
|
|
75
|
+
* - "__strong _object --> NSKeyValueObservance" -> "NSKeyValueObservance"
|
|
76
|
+
* - "<CFDictionary 0x6000029c4840> [64]" -> "CFDictionary"
|
|
77
|
+
* - "<NSMutableDictionary 0x600003ccf5c0> [32]" -> "NSMutableDictionary"
|
|
78
|
+
* - "_object --> <NSObject 0x14f100> [16]" -> "NSObject"
|
|
79
|
+
* - "" -> null (caller skips)
|
|
77
80
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
81
|
+
* The `<ClassName 0xADDR> [size]` form is what `leaks --referenceTree` emits
|
|
82
|
+
* for arrow-targeted instances (e.g. `_object --> <NSObject 0xADDR>`).
|
|
83
|
+
* Without normalization, each address becomes its own aggregation key and
|
|
84
|
+
* the same logical class shows up as N separate rows in the top-N list.
|
|
85
|
+
* Normalizing to the base class name (the token inside the `<...>`) folds
|
|
86
|
+
* those rows back together. This is the v1.10 fix for the gap where
|
|
87
|
+
* AVPlayerItem-style classes appeared aggregated at the root level but
|
|
88
|
+
* NSMutableDictionary-style classes appeared per-address in arrow targets.
|
|
89
|
+
*
|
|
90
|
+
* Excludes c-runtime allocations like `malloc in FigSimpleMutex...` AND the
|
|
91
|
+
* bracketed form `<malloc in ...>` / `<calloc in ...>` / `<realloc in ...>`
|
|
92
|
+
* that leaks emits alongside Obj-C/Swift classes; those are not actionable
|
|
93
|
+
* for abandoned-memory triage and would inflate the top-N list with
|
|
94
|
+
* low-signal entries.
|
|
82
95
|
*/
|
|
83
96
|
export function extractClassName(rawLabel) {
|
|
84
97
|
const label = rawLabel.trim();
|
|
85
98
|
if (label.length === 0)
|
|
86
99
|
return null;
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
// Resolve `--> Target` arrows first: the class name is the value's type,
|
|
101
|
+
// not the property name that points at it. Then run the allocator + size
|
|
102
|
+
// filters against the resolved candidate so entries like
|
|
103
|
+
// `unaligned --> calloc in quic_stream_allocate` are correctly dropped.
|
|
89
104
|
const arrowIdx = label.indexOf("-->");
|
|
90
|
-
|
|
105
|
+
let candidate = arrowIdx >= 0 ? label.slice(arrowIdx + 3).trim() : label;
|
|
91
106
|
if (candidate.length === 0)
|
|
92
107
|
return null;
|
|
108
|
+
// Raw allocator entries (`malloc in ...`, `calloc in ...`) are dropped.
|
|
109
|
+
// Same for the bracketed form: `<malloc in ...>` / `<calloc in ...>`.
|
|
110
|
+
if (/^(malloc|calloc|realloc)\b/i.test(candidate))
|
|
111
|
+
return null;
|
|
112
|
+
if (/^<\s*(malloc|calloc|realloc)\b/i.test(candidate))
|
|
113
|
+
return null;
|
|
114
|
+
// Normalize `<ClassName 0xADDR> [size]` and `<ClassName 0xADDR>` to
|
|
115
|
+
// `ClassName`. The base-class name is the leading token inside `<...>`,
|
|
116
|
+
// before the first space (which separates it from the address).
|
|
117
|
+
const bracketMatch = /^<\s*([^\s<>]+)\s+0x[0-9a-fA-F]+\s*>(?:\s*\[[^\]]+\])?\s*$/.exec(candidate);
|
|
118
|
+
if (bracketMatch) {
|
|
119
|
+
candidate = bracketMatch[1];
|
|
120
|
+
}
|
|
93
121
|
return candidate;
|
|
94
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Pure: returns true when the class name is framework-noise that crowds out
|
|
125
|
+
* actionable classes in the abandoned-memory top-N list. Used by the
|
|
126
|
+
* `analyzeMemgraph` and `analyzeAbandonedMemory` tools to populate a
|
|
127
|
+
* `*Suspects[]` / `actionable*[]` field alongside the raw `*Top[]` field.
|
|
128
|
+
*
|
|
129
|
+
* The list catalogs:
|
|
130
|
+
* - Foundation collection types (`NSMutableDictionary`, `CFString`, etc.)
|
|
131
|
+
* that grow with normal app activity and are rarely the leak itself
|
|
132
|
+
* - ObjC runtime / metadata classes (`Class.data`, `OBJC_METACLASS_$...`)
|
|
133
|
+
* - Apple system frameworks' static data sections (`__DATA __bss`,
|
|
134
|
+
* `__DATA __data`, `__DATA __common`)
|
|
135
|
+
* - `<<TOTAL>>` summary row
|
|
136
|
+
* - "Stack of thread N" and similar meta-rows
|
|
137
|
+
* - Non-object zone descriptors and memory-zone metadata
|
|
138
|
+
*
|
|
139
|
+
* Deliberately NOT noise: AV*, NSKeyValueObserv*, SwiftUI app-level types,
|
|
140
|
+
* Combine, RxSwift, app-named classes, anonymous closures (`<closure ...>`).
|
|
141
|
+
*
|
|
142
|
+
* The default behavior of `analyzeMemgraph` continues to return the raw
|
|
143
|
+
* `abandonedMemoryTop[]` so callers who need framework-collection counts
|
|
144
|
+
* (e.g. cache-bloat investigations) still see them. The actionable field
|
|
145
|
+
* is parallel data, not a replacement.
|
|
146
|
+
*/
|
|
147
|
+
export function isFrameworkNoise(className) {
|
|
148
|
+
// Summary / meta-rows
|
|
149
|
+
if (className === "<<TOTAL>>" || className === "<< TOTAL >>")
|
|
150
|
+
return true;
|
|
151
|
+
if (/^Stack of thread\b/i.test(className))
|
|
152
|
+
return true;
|
|
153
|
+
if (/^non-object\b/i.test(className))
|
|
154
|
+
return true;
|
|
155
|
+
// Memory zones / VM regions
|
|
156
|
+
if (/^VM:\s/.test(className))
|
|
157
|
+
return true;
|
|
158
|
+
if (/(DefaultMallocZone|QuartzCore_0x|MALLOC_)/.test(className))
|
|
159
|
+
return true;
|
|
160
|
+
// ObjC runtime + metadata
|
|
161
|
+
if (/^Class\.(data|methodCache)/.test(className))
|
|
162
|
+
return true;
|
|
163
|
+
if (/^OBJC_METACLASS_/.test(className))
|
|
164
|
+
return true;
|
|
165
|
+
if (/^OBJC_CLASS_/.test(className))
|
|
166
|
+
return true;
|
|
167
|
+
// Apple framework static-data sections
|
|
168
|
+
if (/__DATA\s+__(bss|data|common|objc_data|objc_const)/.test(className))
|
|
169
|
+
return true;
|
|
170
|
+
if (/__DATA_DIRTY\b/.test(className))
|
|
171
|
+
return true;
|
|
172
|
+
if (/dylib\s+__DATA/.test(className))
|
|
173
|
+
return true;
|
|
174
|
+
// Block infrastructure (closures, GCD work items)
|
|
175
|
+
if (className === "__NSMallocBlock__")
|
|
176
|
+
return true;
|
|
177
|
+
if (className === "__NSConcreteMallocBlock")
|
|
178
|
+
return true;
|
|
179
|
+
if (className === "__NSStackBlock__")
|
|
180
|
+
return true;
|
|
181
|
+
if (className === "__NSGlobalBlock__")
|
|
182
|
+
return true;
|
|
183
|
+
// Swift runtime metadata
|
|
184
|
+
if (className === "Swift Metadata")
|
|
185
|
+
return true;
|
|
186
|
+
if (/^Swift\.OpaqueExistentialContainer\b/.test(className))
|
|
187
|
+
return true;
|
|
188
|
+
// GCD infrastructure (queue + group + semaphore types accumulate naturally).
|
|
189
|
+
// No word-boundary because `dispatch_queue_t` / `dispatch_semaphore_t`
|
|
190
|
+
// continue with `_t` after the type name (no `\b` between word chars).
|
|
191
|
+
if (/^dispatch_(queue|group|semaphore|source|io)(_t)?\b/.test(className))
|
|
192
|
+
return true;
|
|
193
|
+
// C++ stdlib bookkeeping
|
|
194
|
+
if (/^std::__shared_ptr_emplace</.test(className))
|
|
195
|
+
return true;
|
|
196
|
+
if (/^std::__1::shared_ptr</.test(className))
|
|
197
|
+
return true;
|
|
198
|
+
// Foundation key/value/map collections (scale with app activity, rarely
|
|
199
|
+
// the actionable leak site)
|
|
200
|
+
if (/^NSMapTable\b/.test(className))
|
|
201
|
+
return true;
|
|
202
|
+
if (/^NSHashTable\b/.test(className))
|
|
203
|
+
return true;
|
|
204
|
+
// Bundle metadata (loads + caches grow naturally with app activity)
|
|
205
|
+
if (className === "NSBundle" || className === "CFBundle")
|
|
206
|
+
return true;
|
|
207
|
+
// Network.framework / XPC / BackBoardServices internal types
|
|
208
|
+
if (/^OS_(nw|xpc|os_lock|object)_/.test(className))
|
|
209
|
+
return true;
|
|
210
|
+
if (/^NWConcrete_/.test(className))
|
|
211
|
+
return true;
|
|
212
|
+
if (/^nw_/.test(className))
|
|
213
|
+
return true;
|
|
214
|
+
if (/^BSObjC/.test(className))
|
|
215
|
+
return true;
|
|
216
|
+
// Font cache infrastructure (CoreText)
|
|
217
|
+
if (className === "UICTFont" || className === "TTenuousComponentFont")
|
|
218
|
+
return true;
|
|
219
|
+
// Block/closure infrastructure that leaks(1) labels generically
|
|
220
|
+
if (className === "Closure context (unknown layout)")
|
|
221
|
+
return true;
|
|
222
|
+
// Array storage (Foundation/CoreFoundation backing storage)
|
|
223
|
+
if (/^NSArray\._list/.test(className))
|
|
224
|
+
return true;
|
|
225
|
+
// SwiftUI internal anonymized buffers (private namespaces ending in dollar-tagged module ids).
|
|
226
|
+
// Real app-level SwiftUI types use module-qualified names like `MyApp.MyView`;
|
|
227
|
+
// these anonymized SwiftUI internals scale with view-graph activity.
|
|
228
|
+
if (/^SwiftUI\.\(.+ in \$[0-9a-f]+\)</.test(className))
|
|
229
|
+
return true;
|
|
230
|
+
// Foundation collection types that grow with normal app activity
|
|
231
|
+
const noiseCollections = new Set([
|
|
232
|
+
"NSMutableDictionary",
|
|
233
|
+
"NSMutableDictionary (Storage)",
|
|
234
|
+
"NSDictionary",
|
|
235
|
+
"NSDictionary (Storage)",
|
|
236
|
+
"NSMutableArray",
|
|
237
|
+
"NSMutableArray (Storage)",
|
|
238
|
+
"NSArray",
|
|
239
|
+
"NSArray (Storage)",
|
|
240
|
+
"NSMutableSet",
|
|
241
|
+
"NSSet",
|
|
242
|
+
"CFString",
|
|
243
|
+
"CFString (Storage)",
|
|
244
|
+
"CFDictionary",
|
|
245
|
+
"CFDictionary (Storage)",
|
|
246
|
+
"CFDictionary (Value Storage)",
|
|
247
|
+
"CFArray",
|
|
248
|
+
"CFSet",
|
|
249
|
+
"CFSet (Value Storage)",
|
|
250
|
+
"CFData",
|
|
251
|
+
"NSData",
|
|
252
|
+
"NSConcreteMutableData",
|
|
253
|
+
"__NSCFString",
|
|
254
|
+
"__NSCFData",
|
|
255
|
+
"__NSCFDictionary",
|
|
256
|
+
"__NSCFArray",
|
|
257
|
+
"NSConcreteAttributedString",
|
|
258
|
+
]);
|
|
259
|
+
if (noiseCollections.has(className))
|
|
260
|
+
return true;
|
|
261
|
+
// Closure contexts and Swift internal storage that scale with app activity
|
|
262
|
+
if (/^Swift\._ContiguousArrayStorage</.test(className))
|
|
263
|
+
return true;
|
|
264
|
+
if (/^Swift\._SwiftDeferredNSDictionary</.test(className))
|
|
265
|
+
return true;
|
|
266
|
+
// Anonymous bracketed instances that still leaked through (extractClassName
|
|
267
|
+
// missed them, e.g. closures with no class name).
|
|
268
|
+
if (/^<[^>]*0x[0-9a-fA-F]+>/.test(className))
|
|
269
|
+
return true;
|
|
270
|
+
// Foundation observer registry internals (these grow with KVO activity but
|
|
271
|
+
// are not the actionable site; the user's class is what to fix).
|
|
272
|
+
if (className === "CFXNotificationRegistrar")
|
|
273
|
+
return true;
|
|
274
|
+
if (/^_realloc in/.test(className))
|
|
275
|
+
return true;
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
95
278
|
/**
|
|
96
279
|
* Pure: parse `leaks --referenceTree --groupByType --noContent` stdout,
|
|
97
280
|
* aggregate instance counts + bytes by class name, return the top N by
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"referenceTree.js","sourceRoot":"","sources":["../../src/parsers/referenceTree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAQH,MAAM,OAAO;AACX,oHAAoH;AACpH,uEAAuE;AACvE,wDAAwD;AACxD,yCAAyC,CAAC;AAE5C,MAAM,IAAI,GAAG,IAAI,CAAC;AAElB;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,2CAA2C,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,KAAK,GAAG;YACN,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;QAClC,KAAK,GAAG;YACN,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QACzC,KAAK,GAAG;YACN,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAChD;YACE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"referenceTree.js","sourceRoot":"","sources":["../../src/parsers/referenceTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* agent loop without an extra round-trip.
|
|
19
19
|
*/
|
|
20
20
|
import { z } from "zod";
|
|
21
|
-
export declare const outputFormatField: z.ZodOptional<z.ZodEnum<["markdown", "json", "both"]>>;
|
|
21
|
+
export declare const outputFormatField: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
|
|
22
22
|
export type OutputFormat = z.infer<typeof outputFormatField>;
|
|
23
23
|
export interface McpContentItem {
|
|
24
24
|
type: "text";
|
|
@@ -41,6 +41,29 @@ export interface McpResponse {
|
|
|
41
41
|
* `markdown` and `both`, the markdown is rendered via {@link renderAsMarkdown}.
|
|
42
42
|
*/
|
|
43
43
|
export declare function formatMcpResponse(result: unknown, toolName: string, format: OutputFormat | undefined): McpResponse;
|
|
44
|
+
/**
|
|
45
|
+
* Pure: render a verify-fix focused markdown table for tools that support
|
|
46
|
+
* it. Returns `null` if the tool's result does not match the expected
|
|
47
|
+
* verify-fix shape, signaling the caller to fall back to standard markdown.
|
|
48
|
+
*
|
|
49
|
+
* Supported tools:
|
|
50
|
+
*
|
|
51
|
+
* - `analyzeAbandonedMemory`: reads `actionableShrinkage[]` (the v1.10
|
|
52
|
+
* verify-fix-default direction: classes that the fix freed) and
|
|
53
|
+
* `actionableGrowth[]` (regressions the fix didn't address). Emits one
|
|
54
|
+
* table for shrinkage and, when non-empty, a second smaller table for
|
|
55
|
+
* growth. Threshold: |delta| >= 10 by default to filter cosmetic noise.
|
|
56
|
+
*
|
|
57
|
+
* - `diffMemgraphs`: reads `classCountChanges[]` (positive + negative).
|
|
58
|
+
* Future expansion; for now returns null and falls back to standard
|
|
59
|
+
* markdown.
|
|
60
|
+
*
|
|
61
|
+
* The 4-column layout is deliberately compact (Class | Before | After |
|
|
62
|
+
* Delta) so it renders cleanly in GitHub's markdown preview, dev.to, and
|
|
63
|
+
* agent chat contexts. A trailing `> Diagnosis: ...` blockquote carries
|
|
64
|
+
* the structured `diagnosis` field when present.
|
|
65
|
+
*/
|
|
66
|
+
export declare function renderVerifyFixTable(result: unknown, toolName: string): string | null;
|
|
44
67
|
/**
|
|
45
68
|
* Pure: render an arbitrary JSON-shaped value as markdown.
|
|
46
69
|
*
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
import { z } from "zod";
|
|
21
21
|
import { getRedactionMode, redact } from "./redact.js";
|
|
22
22
|
export const outputFormatField = z
|
|
23
|
-
.enum(["markdown", "json", "both"])
|
|
23
|
+
.enum(["markdown", "json", "both", "verify-fix-table"])
|
|
24
24
|
.optional()
|
|
25
|
-
.describe("Response format. Omitted or `json` (default, preserves v1.8 behavior) returns JSON.stringify of the result. `markdown` renders a human-readable view of the same data. `both` returns both content items in one response, so a client can display markdown to the user and parse JSON for the agent loop without a second call.");
|
|
25
|
+
.describe("Response format. Omitted or `json` (default, preserves v1.8 behavior) returns JSON.stringify of the result. `markdown` renders a human-readable view of the same data. `both` returns both content items in one response, so a client can display markdown to the user and parse JSON for the agent loop without a second call. `verify-fix-table` (v1.10, applies to `analyzeAbandonedMemory` and `diffMemgraphs`) emits a focused 4-column markdown comparison table (Class | Before | After | Delta) of the actionable rows; other tools fall back to `markdown` for this value.");
|
|
26
26
|
/**
|
|
27
27
|
* Pure: shape the MCP response based on the caller's `outputFormat`.
|
|
28
28
|
*
|
|
@@ -41,6 +41,18 @@ export function formatMcpResponse(result, toolName, format) {
|
|
|
41
41
|
if (mode === "json") {
|
|
42
42
|
return { content: [{ type: "text", text: json }] };
|
|
43
43
|
}
|
|
44
|
+
// `verify-fix-table` is a focused renderer that takes precedence over
|
|
45
|
+
// the generic markdown one. Falls back to standard markdown when the
|
|
46
|
+
// tool does not implement a verify-fix view.
|
|
47
|
+
if (mode === "verify-fix-table") {
|
|
48
|
+
const focused = renderVerifyFixTable(redacted, toolName);
|
|
49
|
+
if (focused != null) {
|
|
50
|
+
return { content: [{ type: "text", text: focused }] };
|
|
51
|
+
}
|
|
52
|
+
// Fall through to markdown for tools that don't implement it.
|
|
53
|
+
const fallback = renderAsMarkdown(redacted, toolName);
|
|
54
|
+
return { content: [{ type: "text", text: fallback }] };
|
|
55
|
+
}
|
|
44
56
|
const markdown = renderAsMarkdown(redacted, toolName);
|
|
45
57
|
if (mode === "markdown") {
|
|
46
58
|
return { content: [{ type: "text", text: markdown }] };
|
|
@@ -55,6 +67,101 @@ export function formatMcpResponse(result, toolName, format) {
|
|
|
55
67
|
],
|
|
56
68
|
};
|
|
57
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Pure: render a verify-fix focused markdown table for tools that support
|
|
72
|
+
* it. Returns `null` if the tool's result does not match the expected
|
|
73
|
+
* verify-fix shape, signaling the caller to fall back to standard markdown.
|
|
74
|
+
*
|
|
75
|
+
* Supported tools:
|
|
76
|
+
*
|
|
77
|
+
* - `analyzeAbandonedMemory`: reads `actionableShrinkage[]` (the v1.10
|
|
78
|
+
* verify-fix-default direction: classes that the fix freed) and
|
|
79
|
+
* `actionableGrowth[]` (regressions the fix didn't address). Emits one
|
|
80
|
+
* table for shrinkage and, when non-empty, a second smaller table for
|
|
81
|
+
* growth. Threshold: |delta| >= 10 by default to filter cosmetic noise.
|
|
82
|
+
*
|
|
83
|
+
* - `diffMemgraphs`: reads `classCountChanges[]` (positive + negative).
|
|
84
|
+
* Future expansion; for now returns null and falls back to standard
|
|
85
|
+
* markdown.
|
|
86
|
+
*
|
|
87
|
+
* The 4-column layout is deliberately compact (Class | Before | After |
|
|
88
|
+
* Delta) so it renders cleanly in GitHub's markdown preview, dev.to, and
|
|
89
|
+
* agent chat contexts. A trailing `> Diagnosis: ...` blockquote carries
|
|
90
|
+
* the structured `diagnosis` field when present.
|
|
91
|
+
*/
|
|
92
|
+
export function renderVerifyFixTable(result, toolName) {
|
|
93
|
+
if (toolName !== "analyzeAbandonedMemory") {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (result == null || typeof result !== "object") {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const obj = result;
|
|
100
|
+
const shrinkage = extractVerifyFixRows(obj["actionableShrinkage"]);
|
|
101
|
+
const growth = extractVerifyFixRows(obj["actionableGrowth"]);
|
|
102
|
+
const diagnosis = typeof obj["diagnosis"] === "string" ? obj["diagnosis"] : null;
|
|
103
|
+
// Threshold: filter cosmetic noise.
|
|
104
|
+
const DELTA_THRESHOLD = 10;
|
|
105
|
+
const filteredShrinkage = shrinkage.filter((r) => Math.abs(r.delta) >= DELTA_THRESHOLD);
|
|
106
|
+
const filteredGrowth = growth.filter((r) => Math.abs(r.delta) >= DELTA_THRESHOLD);
|
|
107
|
+
if (filteredShrinkage.length === 0 && filteredGrowth.length === 0) {
|
|
108
|
+
return [
|
|
109
|
+
"# analyzeAbandonedMemory: verify-fix",
|
|
110
|
+
"",
|
|
111
|
+
"_No class counts crossed the actionable threshold (|delta| >= 10)._",
|
|
112
|
+
diagnosis ? `\n> ${diagnosis}` : "",
|
|
113
|
+
]
|
|
114
|
+
.join("\n")
|
|
115
|
+
.trim();
|
|
116
|
+
}
|
|
117
|
+
const sections = ["# analyzeAbandonedMemory: verify-fix", ""];
|
|
118
|
+
if (filteredShrinkage.length > 0) {
|
|
119
|
+
sections.push("## What the fix freed");
|
|
120
|
+
sections.push("");
|
|
121
|
+
sections.push("| Class | Before | After | Delta |");
|
|
122
|
+
sections.push("|---|---:|---:|---:|");
|
|
123
|
+
for (const row of filteredShrinkage) {
|
|
124
|
+
sections.push(`| \`${row.className}\` | ${row.beforeCount} | ${row.afterCount} | ${row.delta} |`);
|
|
125
|
+
}
|
|
126
|
+
sections.push("");
|
|
127
|
+
}
|
|
128
|
+
if (filteredGrowth.length > 0) {
|
|
129
|
+
sections.push("## Classes that grew (regressions or unrelated)");
|
|
130
|
+
sections.push("");
|
|
131
|
+
sections.push("| Class | Before | After | Delta |");
|
|
132
|
+
sections.push("|---|---:|---:|---:|");
|
|
133
|
+
for (const row of filteredGrowth) {
|
|
134
|
+
sections.push(`| \`${row.className}\` | ${row.beforeCount} | ${row.afterCount} | +${row.delta} |`);
|
|
135
|
+
}
|
|
136
|
+
sections.push("");
|
|
137
|
+
}
|
|
138
|
+
if (diagnosis) {
|
|
139
|
+
sections.push(`> ${diagnosis}`);
|
|
140
|
+
}
|
|
141
|
+
return sections.join("\n").trim();
|
|
142
|
+
}
|
|
143
|
+
function extractVerifyFixRows(value) {
|
|
144
|
+
if (!Array.isArray(value))
|
|
145
|
+
return [];
|
|
146
|
+
const rows = [];
|
|
147
|
+
for (const item of value) {
|
|
148
|
+
if (item == null || typeof item !== "object")
|
|
149
|
+
continue;
|
|
150
|
+
const r = item;
|
|
151
|
+
if (typeof r["className"] === "string" &&
|
|
152
|
+
typeof r["beforeCount"] === "number" &&
|
|
153
|
+
typeof r["afterCount"] === "number" &&
|
|
154
|
+
typeof r["delta"] === "number") {
|
|
155
|
+
rows.push({
|
|
156
|
+
className: r["className"],
|
|
157
|
+
beforeCount: r["beforeCount"],
|
|
158
|
+
afterCount: r["afterCount"],
|
|
159
|
+
delta: r["delta"],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return rows;
|
|
164
|
+
}
|
|
58
165
|
/**
|
|
59
166
|
* Pure: render an arbitrary JSON-shaped value as markdown.
|
|
60
167
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responseFormatter.js","sourceRoot":"","sources":["../../src/runtime/responseFormatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"responseFormatter.js","sourceRoot":"","sources":["../../src/runtime/responseFormatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,24 +29,24 @@ export declare const analyzeAbandonedMemoryShape: {
|
|
|
29
29
|
readonly afterPath: z.ZodString;
|
|
30
30
|
readonly topN: z.ZodDefault<z.ZodNumber>;
|
|
31
31
|
readonly classFilter: z.ZodOptional<z.ZodString>;
|
|
32
|
-
readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both"]>>;
|
|
32
|
+
readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
|
|
33
33
|
};
|
|
34
34
|
export declare const analyzeAbandonedMemorySchema: z.ZodObject<{
|
|
35
35
|
readonly beforePath: z.ZodString;
|
|
36
36
|
readonly afterPath: z.ZodString;
|
|
37
37
|
readonly topN: z.ZodDefault<z.ZodNumber>;
|
|
38
38
|
readonly classFilter: z.ZodOptional<z.ZodString>;
|
|
39
|
-
readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both"]>>;
|
|
39
|
+
readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
|
|
40
40
|
}, "strip", z.ZodTypeAny, {
|
|
41
41
|
topN: number;
|
|
42
42
|
beforePath: string;
|
|
43
43
|
afterPath: string;
|
|
44
|
-
outputFormat?: "markdown" | "json" | "both" | undefined;
|
|
44
|
+
outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
|
|
45
45
|
classFilter?: string | undefined;
|
|
46
46
|
}, {
|
|
47
47
|
beforePath: string;
|
|
48
48
|
afterPath: string;
|
|
49
|
-
outputFormat?: "markdown" | "json" | "both" | undefined;
|
|
49
|
+
outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
|
|
50
50
|
topN?: number | undefined;
|
|
51
51
|
classFilter?: string | undefined;
|
|
52
52
|
}>;
|
|
@@ -80,14 +80,36 @@ export interface AnalyzeAbandonedMemoryResult {
|
|
|
80
80
|
* descending. Each entry carries a `classification` from the catalog plus
|
|
81
81
|
* a `confidence` tier. The agent can branch on `classification` to choose
|
|
82
82
|
* the right `swiftSearchPattern` / fix template.
|
|
83
|
+
*
|
|
84
|
+
* **Raw view.** Includes framework noise (NSMutableDictionary, CFString,
|
|
85
|
+
* libMainThreadChecker bss, etc.). Useful for cache-bloat investigations.
|
|
83
86
|
*/
|
|
84
87
|
growthByClass: AbandonedMemoryEntry[];
|
|
85
88
|
/**
|
|
86
89
|
* Classes that shrunk between before and after. Surfaced so the caller
|
|
87
90
|
* can confirm the fix freed the suspect class (e.g. AVPlayerItem in the
|
|
88
91
|
* notelet case went from 342 to 0). Sorted by absolute delta desc.
|
|
92
|
+
*
|
|
93
|
+
* **Raw view.** See `actionableShrinkage` for the filtered "what fix
|
|
94
|
+
* verifiably freed" view.
|
|
89
95
|
*/
|
|
90
96
|
shrinkageByClass: AbandonedMemoryEntry[];
|
|
97
|
+
/**
|
|
98
|
+
* `growthByClass` with framework noise filtered out (Foundation collection
|
|
99
|
+
* types, ObjC metadata, __DATA sections, allocator stacks, etc.). The
|
|
100
|
+
* remaining entries are user-actionable classes. New in v1.10.
|
|
101
|
+
*
|
|
102
|
+
* Use this when answering "what new bug just appeared?". Use the raw
|
|
103
|
+
* `growthByClass` when answering "what does the heap look like now?".
|
|
104
|
+
*/
|
|
105
|
+
actionableGrowth: AbandonedMemoryEntry[];
|
|
106
|
+
/**
|
|
107
|
+
* `shrinkageByClass` with framework noise filtered out. Use this in the
|
|
108
|
+
* verify-fix loop to confirm which app-level classes the fix actually
|
|
109
|
+
* freed. AVPlayerItem dropping from 342 to 0 shows up here at the top.
|
|
110
|
+
* New in v1.10.
|
|
111
|
+
*/
|
|
112
|
+
actionableShrinkage: AbandonedMemoryEntry[];
|
|
91
113
|
/** Plain-English diagnosis tying the highest-confidence growth to a fix hint. */
|
|
92
114
|
diagnosis: string;
|
|
93
115
|
/** Pipeline hints: chain into `swiftSearchPattern` against the top growth class. */
|
|
@@ -25,7 +25,7 @@ import { z } from "zod";
|
|
|
25
25
|
import { existsSync } from "node:fs";
|
|
26
26
|
import { resolve as resolvePath } from "node:path";
|
|
27
27
|
import { runCommand } from "../runtime/exec.js";
|
|
28
|
-
import { parseReferenceTreeText, } from "../parsers/referenceTree.js";
|
|
28
|
+
import { parseReferenceTreeText, isFrameworkNoise, } from "../parsers/referenceTree.js";
|
|
29
29
|
import { outputFormatField } from "../runtime/responseFormatter.js";
|
|
30
30
|
export const analyzeAbandonedMemoryShape = {
|
|
31
31
|
beforePath: z
|
|
@@ -137,10 +137,23 @@ export function buildAbandonedMemoryDiff(before, after, options) {
|
|
|
137
137
|
};
|
|
138
138
|
const diagnosis = buildDiagnosis(growthByClass, shrinkageByClass);
|
|
139
139
|
const suggestedNextCalls = buildSuggestedNextCalls(growthByClass);
|
|
140
|
+
// Actionable views: drop framework noise so the caller's first-look list
|
|
141
|
+
// surfaces app-level + AV + KVO classes instead of NSMutableDictionary +
|
|
142
|
+
// CFString + ObjC runtime data. Same ranking, just filtered. Falls back
|
|
143
|
+
// to topN, so the actionable list can be SHORTER than the raw view when
|
|
144
|
+
// most of the top entries are noise.
|
|
145
|
+
const actionableGrowth = growthByClass
|
|
146
|
+
.filter((e) => !isFrameworkNoise(e.className))
|
|
147
|
+
.slice(0, options.topN);
|
|
148
|
+
const actionableShrinkage = shrinkageByClass
|
|
149
|
+
.filter((e) => !isFrameworkNoise(e.className))
|
|
150
|
+
.slice(0, options.topN);
|
|
140
151
|
return {
|
|
141
152
|
totals,
|
|
142
153
|
growthByClass: growthByClass.slice(0, options.topN),
|
|
143
154
|
shrinkageByClass: shrinkageByClass.slice(0, options.topN),
|
|
155
|
+
actionableGrowth,
|
|
156
|
+
actionableShrinkage,
|
|
144
157
|
diagnosis,
|
|
145
158
|
...(suggestedNextCalls.length > 0 ? { suggestedNextCalls } : {}),
|
|
146
159
|
};
|
|
@@ -181,13 +194,38 @@ export function classifyGrowth(className, delta, hasKvoCoOccurrence, kvoObservan
|
|
|
181
194
|
hint: "NSKeyValueObservance growth indicates `observe(\\.x) { ... }` tokens that were never invalidated. The token strongly retains the change closure (which usually captures self), and the closure is anchored in the KVO global observer registry. Use `[weak self]` inside the observe closure and call `token?.invalidate()` in `deinit`, or invalidate-then-nil before reassigning the token. See `classifyCycle` pattern `kvo.observation-not-invalidated`.",
|
|
182
195
|
};
|
|
183
196
|
}
|
|
197
|
+
// KVO co-occurrence escalation. Tightened in v1.10 after the v1.9 retro
|
|
198
|
+
// showed the broad `delta >= 5` rule was painting 25 unrelated classes
|
|
199
|
+
// as `kvo-observer-orphaned high` whenever NSKeyValueObservance grew at
|
|
200
|
+
// all. Three guards now:
|
|
201
|
+
// 1. Skip classes that look like framework noise (allocator stacks,
|
|
202
|
+
// memory zones, __DATA sections, summary rows, etc.)
|
|
203
|
+
// 2. Skip classes that are not "object-shaped" (anonymous bracket
|
|
204
|
+
// forms `<... 0xADDR> [size]`, byte-offset prefixes, etc.). These
|
|
205
|
+
// slip past Phase A's extractClassName when the leaks output uses
|
|
206
|
+
// a non-canonical form for them.
|
|
207
|
+
// 3. Require the candidate's delta to be a meaningful FRACTION of
|
|
208
|
+
// the KVO growth, not just any positive delta. A class with
|
|
209
|
+
// delta=5 when NSKeyValueObservance grew by +200 is statistical
|
|
210
|
+
// noise; the proportion thresholds below capture this.
|
|
184
211
|
if (hasKvoCoOccurrence && delta >= 5) {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
212
|
+
const isObjectShaped = !isFrameworkNoise(className) &&
|
|
213
|
+
!/^<.*0x[0-9a-fA-F]+.*>/.test(className) &&
|
|
214
|
+
!/^\d+ bytes into\b/.test(className);
|
|
215
|
+
// Proportional thresholds. A class whose delta is below half of the
|
|
216
|
+
// NSKeyValueObservance delta is rejected as too small to be the
|
|
217
|
+
// observed type. Above 5x the KVO delta the confidence escalates
|
|
218
|
+
// to high (the class is the dominant observed type by a wide margin).
|
|
219
|
+
const mediumThreshold = Math.max(5, Math.floor(kvoObservanceDelta * 0.5));
|
|
220
|
+
const highThreshold = Math.max(50, Math.floor(kvoObservanceDelta * 5));
|
|
221
|
+
if (isObjectShaped && delta >= mediumThreshold) {
|
|
222
|
+
const confidence = delta >= highThreshold ? "high" : "medium";
|
|
223
|
+
return {
|
|
224
|
+
classification: "kvo-observer-orphaned",
|
|
225
|
+
confidence,
|
|
226
|
+
hint: `Co-occurring NSKeyValueObservance growth (+${kvoObservanceDelta}) suggests this type is the value being observed via \`observe(\\.x) { ... }\`. The orphaned observer holds the value alive. Fixing the observer (\`token.invalidate()\` on teardown) will free this class too. See \`classifyCycle\` pattern \`kvo.observation-not-invalidated\`.`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
191
229
|
}
|
|
192
230
|
if (/^NS(Cache|CountedSet|MapTable|MutableArray|MutableDictionary|MutableSet|HashTable)/.test(className)) {
|
|
193
231
|
return {
|