memorydetective 1.12.0 → 1.14.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +15 -5
  3. package/USAGE.md +29 -0
  4. package/dist/index.js +20 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/parsers/schemaDiscovery.d.ts +88 -0
  7. package/dist/parsers/schemaDiscovery.js +144 -0
  8. package/dist/parsers/schemaDiscovery.js.map +1 -0
  9. package/dist/parsers/xctraceXml.d.ts +5 -0
  10. package/dist/parsers/xctraceXml.js +3 -0
  11. package/dist/parsers/xctraceXml.js.map +1 -1
  12. package/dist/runtime/prompts.js +49 -0
  13. package/dist/runtime/prompts.js.map +1 -1
  14. package/dist/tools/analyzeAllocations.d.ts +5 -1
  15. package/dist/tools/analyzeAllocations.js +17 -1
  16. package/dist/tools/analyzeAllocations.js.map +1 -1
  17. package/dist/tools/analyzeAnimationHitches.d.ts +5 -1
  18. package/dist/tools/analyzeAnimationHitches.js +17 -1
  19. package/dist/tools/analyzeAnimationHitches.js.map +1 -1
  20. package/dist/tools/analyzeAppLaunch.d.ts +3 -0
  21. package/dist/tools/analyzeAppLaunch.js +17 -1
  22. package/dist/tools/analyzeAppLaunch.js.map +1 -1
  23. package/dist/tools/analyzeHangs.d.ts +63 -3
  24. package/dist/tools/analyzeHangs.js +143 -19
  25. package/dist/tools/analyzeHangs.js.map +1 -1
  26. package/dist/tools/analyzeNetworkActivity.d.ts +99 -0
  27. package/dist/tools/analyzeNetworkActivity.js +312 -0
  28. package/dist/tools/analyzeNetworkActivity.js.map +1 -0
  29. package/dist/tools/analyzeTimeProfile.d.ts +10 -1
  30. package/dist/tools/analyzeTimeProfile.js +63 -8
  31. package/dist/tools/analyzeTimeProfile.js.map +1 -1
  32. package/dist/tools/captureScenarioState.d.ts +2 -2
  33. package/dist/tools/countAlive.d.ts +35 -1
  34. package/dist/tools/countAlive.js +124 -29
  35. package/dist/tools/countAlive.js.map +1 -1
  36. package/dist/tools/inspectTrace.js +112 -18
  37. package/dist/tools/inspectTrace.js.map +1 -1
  38. package/dist/tools/recordTimeProfile.d.ts +83 -0
  39. package/dist/tools/recordTimeProfile.js +135 -0
  40. package/dist/tools/recordTimeProfile.js.map +1 -1
  41. package/dist/tools/replayScenario.d.ts +4 -4
  42. package/dist/tools/summarizeTrace.d.ts +147 -0
  43. package/dist/tools/summarizeTrace.js +424 -0
  44. package/dist/tools/summarizeTrace.js.map +1 -0
  45. package/dist/tools/verifyFix.d.ts +27 -0
  46. package/dist/tools/verifyFix.js +78 -4
  47. package/dist/tools/verifyFix.js.map +1 -1
  48. package/dist/types.d.ts +28 -0
  49. package/package.json +2 -2
@@ -0,0 +1,312 @@
1
+ /**
2
+ * `analyzeNetworkActivity`: parses xctrace's network-connections schema
3
+ * from a `.trace` recorded with the Network Profile template. v1.14 item A.
4
+ *
5
+ * "The network is slow" / "my SDK is chatty" / "slow launch because of
6
+ * one API call" are top-3 iOS perf complaints. Pre-v1.14 we had zero
7
+ * coverage of the network family. XcodeTraceMCP's regex map listed it
8
+ * as one of their five instrument families. This analyzer closes the
9
+ * gap with the same shape as analyzeHangs / analyzeAnimationHitches:
10
+ *
11
+ * - Bytes-in/out, duration, status-code, and URL/host extracted per
12
+ * connection.
13
+ * - Aggregates: total bytes, slowest response, average response, count
14
+ * per HTTP status bucket.
15
+ * - Top-N by duration (the "which calls blocked the user?" view) and
16
+ * top-N by bytes (the "which calls are bloating my budget?" view).
17
+ * - Per-host aggregates surfacing chatty SDKs without manually grouping.
18
+ *
19
+ * Resilient to column-name drift across xctrace versions: each field
20
+ * is looked up under multiple plausible mnemonics, falling back to the
21
+ * one that yields data.
22
+ */
23
+ import { z } from "zod";
24
+ import { existsSync } from "node:fs";
25
+ import { resolve as resolvePath } from "node:path";
26
+ import { runCommand } from "../runtime/exec.js";
27
+ import { fetchDiscoveredSchemas } from "../parsers/schemaDiscovery.js";
28
+ import { parseXctraceXml, asNumber, asFormatted, } from "../parsers/xctraceXml.js";
29
+ import { outputFormatField } from "../runtime/responseFormatter.js";
30
+ export const analyzeNetworkActivitySchema = z.object({
31
+ tracePath: z
32
+ .string()
33
+ .min(1)
34
+ .describe("Absolute path to a `.trace` bundle recorded with a Network template (`xcrun xctrace record --template 'Network Profile' --attach <app|pid>`)."),
35
+ topN: z
36
+ .number()
37
+ .int()
38
+ .positive()
39
+ .default(10)
40
+ .describe("Return the top N rows for each ranking dimension (by-duration + by-bytes). Default 10."),
41
+ minBytes: z
42
+ .number()
43
+ .nonnegative()
44
+ .default(0)
45
+ .describe("Filter out connections that transferred fewer than this many bytes (in + out combined). Useful for cutting tiny pings out of the by-bytes view."),
46
+ outputFormat: outputFormatField,
47
+ });
48
+ /**
49
+ * Helper: pull a string field from a row trying multiple plausible
50
+ * column names. xctrace varies network column mnemonics across iOS
51
+ * versions and templates; we try them all rather than failing on a
52
+ * single canonical name.
53
+ */
54
+ function pickString(row, keys) {
55
+ for (const k of keys) {
56
+ const v = asFormatted(row[k]);
57
+ if (v && v.trim().length > 0)
58
+ return v.trim();
59
+ }
60
+ return undefined;
61
+ }
62
+ /** Same idea as pickString but for numeric fields. */
63
+ function pickNumber(row, keys) {
64
+ for (const k of keys) {
65
+ const v = asNumber(row[k]);
66
+ if (typeof v === "number" && Number.isFinite(v))
67
+ return v;
68
+ }
69
+ return undefined;
70
+ }
71
+ /** Extract a host string from a URL-or-host value. Falls back to the
72
+ * raw input when it does not look like a URL (already a host). */
73
+ export function extractHost(urlOrHost) {
74
+ if (!urlOrHost)
75
+ return undefined;
76
+ // Strip scheme + path. Accepts http://host:port/path, host:port/path, bare host.
77
+ const stripped = urlOrHost
78
+ .replace(/^https?:\/\//i, "")
79
+ .replace(/^.*?:\/\//, "");
80
+ const slash = stripped.indexOf("/");
81
+ const hostPort = slash >= 0 ? stripped.slice(0, slash) : stripped;
82
+ // Drop the trailing :port for cleaner aggregation.
83
+ const colon = hostPort.indexOf(":");
84
+ return colon >= 0 ? hostPort.slice(0, colon) : hostPort;
85
+ }
86
+ function statusBucket(code) {
87
+ if (code == null)
88
+ return "n/a";
89
+ if (code >= 200 && code < 300)
90
+ return "2xx";
91
+ if (code >= 300 && code < 400)
92
+ return "3xx";
93
+ if (code >= 400 && code < 500)
94
+ return "4xx";
95
+ if (code >= 500 && code < 600)
96
+ return "5xx";
97
+ return "other";
98
+ }
99
+ /** Pure: turn the network-connections XML into the analyzed result. */
100
+ export function analyzeNetworkActivityFromXml(xml, tracePath, topN = 10, minBytes = 0) {
101
+ const tables = parseXctraceXml(xml);
102
+ // The Network template historically uses "network-connections"; some
103
+ // older builds use plain "network". Both are covered by SCHEMA_FAMILIES
104
+ // network -> the discovered schema name was passed by the caller, but
105
+ // the table here may carry either canonical name when synthetic
106
+ // fixtures are in play. Accept whichever matches.
107
+ const table = tables.find((t) => /network/i.test(t.schema) || /connection/i.test(t.schema) || /^http/i.test(t.schema));
108
+ if (!table) {
109
+ return {
110
+ ok: true,
111
+ tracePath,
112
+ totals: {
113
+ rows: 0,
114
+ totalBytesIn: 0,
115
+ totalBytesOut: 0,
116
+ longestMs: 0,
117
+ averageMs: 0,
118
+ statusBuckets: {},
119
+ },
120
+ topByDuration: [],
121
+ topByBytes: [],
122
+ byHost: [],
123
+ diagnosis: "No network-connections table found in the trace.",
124
+ status: "not_present",
125
+ supportStatus: [
126
+ {
127
+ kind: "network-connections",
128
+ status: "not_present",
129
+ reason: "Schema absent from the trace TOC.",
130
+ },
131
+ ],
132
+ };
133
+ }
134
+ const entries = [];
135
+ for (const row of table.rows) {
136
+ const startNs = pickNumber(row, ["start", "time", "connect-time", "event-time"]) ?? 0;
137
+ const durationNs = pickNumber(row, [
138
+ "duration",
139
+ "response-time",
140
+ "transaction-duration",
141
+ "elapsed",
142
+ ]);
143
+ const url = pickString(row, ["url", "host", "endpoint", "connect-event-host", "request-url"]);
144
+ const method = pickString(row, ["method", "http-method", "request-method"]);
145
+ const statusCode = pickNumber(row, [
146
+ "status-code",
147
+ "http-status",
148
+ "response-code",
149
+ "status",
150
+ ]);
151
+ const bytesIn = pickNumber(row, [
152
+ "bytes-in",
153
+ "response-bytes",
154
+ "received-bytes",
155
+ "bytes-received",
156
+ "rx-bytes",
157
+ ]);
158
+ const bytesOut = pickNumber(row, [
159
+ "bytes-out",
160
+ "request-bytes",
161
+ "sent-bytes",
162
+ "bytes-sent",
163
+ "tx-bytes",
164
+ ]);
165
+ const totalBytes = (bytesIn ?? 0) + (bytesOut ?? 0);
166
+ if (totalBytes < minBytes)
167
+ continue;
168
+ entries.push({
169
+ startNs,
170
+ ...(asFormatted(row.start) ? { startFmt: asFormatted(row.start) } : {}),
171
+ ...(durationNs != null ? { durationNs, durationMs: durationNs / 1_000_000 } : {}),
172
+ ...(asFormatted(row.duration) ? { durationFmt: asFormatted(row.duration) } : {}),
173
+ ...(url ? { url, host: extractHost(url) } : {}),
174
+ ...(method ? { method } : {}),
175
+ ...(statusCode != null ? { statusCode } : {}),
176
+ ...(bytesIn != null ? { bytesIn } : {}),
177
+ ...(bytesOut != null ? { bytesOut } : {}),
178
+ });
179
+ }
180
+ const totalBytesIn = entries.reduce((s, e) => s + (e.bytesIn ?? 0), 0);
181
+ const totalBytesOut = entries.reduce((s, e) => s + (e.bytesOut ?? 0), 0);
182
+ const durations = entries
183
+ .map((e) => e.durationMs ?? 0)
184
+ .filter((d) => d > 0);
185
+ const longestMs = durations.length > 0 ? Math.max(...durations) : 0;
186
+ const averageMs = durations.length > 0
187
+ ? durations.reduce((a, b) => a + b, 0) / durations.length
188
+ : 0;
189
+ const statusBuckets = {};
190
+ for (const e of entries) {
191
+ const bucket = statusBucket(e.statusCode);
192
+ statusBuckets[bucket] = (statusBuckets[bucket] ?? 0) + 1;
193
+ }
194
+ const topByDuration = [...entries]
195
+ .sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0))
196
+ .slice(0, topN);
197
+ const topByBytes = [...entries]
198
+ .sort((a, b) => (b.bytesIn ?? 0) + (b.bytesOut ?? 0) - ((a.bytesIn ?? 0) + (a.bytesOut ?? 0)))
199
+ .slice(0, topN);
200
+ const hostMap = new Map();
201
+ for (const e of entries) {
202
+ if (!e.host)
203
+ continue;
204
+ const cur = hostMap.get(e.host) ?? {
205
+ host: e.host,
206
+ count: 0,
207
+ bytesIn: 0,
208
+ bytesOut: 0,
209
+ longestMs: 0,
210
+ };
211
+ cur.count += 1;
212
+ cur.bytesIn += e.bytesIn ?? 0;
213
+ cur.bytesOut += e.bytesOut ?? 0;
214
+ if (e.durationMs != null && e.durationMs > cur.longestMs) {
215
+ cur.longestMs = e.durationMs;
216
+ }
217
+ hostMap.set(e.host, cur);
218
+ }
219
+ const byHost = Array.from(hostMap.values()).sort((a, b) => b.count - a.count);
220
+ return {
221
+ ok: true,
222
+ tracePath,
223
+ totals: {
224
+ rows: entries.length,
225
+ totalBytesIn,
226
+ totalBytesOut,
227
+ longestMs,
228
+ averageMs,
229
+ statusBuckets,
230
+ },
231
+ topByDuration,
232
+ topByBytes,
233
+ byHost,
234
+ diagnosis: buildDiagnosis(entries.length, longestMs, totalBytesIn, totalBytesOut, byHost),
235
+ status: "available",
236
+ supportStatus: [
237
+ {
238
+ kind: "network-connections",
239
+ status: "available",
240
+ sourceSchemas: ["network-connections"],
241
+ },
242
+ ],
243
+ };
244
+ }
245
+ function buildDiagnosis(rows, longestMs, totalBytesIn, totalBytesOut, byHost) {
246
+ if (rows === 0) {
247
+ return "No network activity in the recording window (or all rows were filtered out by minBytes).";
248
+ }
249
+ const parts = [];
250
+ parts.push(`${rows} network requests captured.`);
251
+ const totalKB = (totalBytesIn + totalBytesOut) / 1024;
252
+ if (totalKB >= 1) {
253
+ parts.push(`${totalKB.toFixed(1)} KB total (${(totalBytesIn / 1024).toFixed(1)} KB in, ${(totalBytesOut / 1024).toFixed(1)} KB out).`);
254
+ }
255
+ if (longestMs > 0) {
256
+ parts.push(`Slowest: ${longestMs.toFixed(0)}ms.`);
257
+ }
258
+ if (byHost.length > 0) {
259
+ const top = byHost[0];
260
+ parts.push(`Chattiest host: \`${top.host}\` (${top.count} requests).`);
261
+ }
262
+ if (longestMs > 3000) {
263
+ parts.push("At least one request over 3s. Likely the user-visible perf gap.");
264
+ }
265
+ return parts.join(" ");
266
+ }
267
+ export async function analyzeNetworkActivity(input) {
268
+ const tracePath = resolvePath(input.tracePath);
269
+ if (!existsSync(tracePath)) {
270
+ throw new Error(`Trace bundle not found: ${tracePath}`);
271
+ }
272
+ const { network: schemaName } = await fetchDiscoveredSchemas(runCommand, tracePath, ["network"]);
273
+ const result = await runCommand("xcrun", [
274
+ "xctrace",
275
+ "export",
276
+ "--input",
277
+ tracePath,
278
+ "--xpath",
279
+ `/trace-toc/run/data/table[@schema="${schemaName}"]`,
280
+ ], { timeoutMs: 5 * 60_000 });
281
+ if (result.code !== 0) {
282
+ // Schema may simply not be present in this trace (e.g. Time Profiler
283
+ // template without Network instrument). Surface as "not_present"
284
+ // rather than throwing so summarizeTrace can branch on status.
285
+ return {
286
+ ok: true,
287
+ tracePath,
288
+ totals: {
289
+ rows: 0,
290
+ totalBytesIn: 0,
291
+ totalBytesOut: 0,
292
+ longestMs: 0,
293
+ averageMs: 0,
294
+ statusBuckets: {},
295
+ },
296
+ topByDuration: [],
297
+ topByBytes: [],
298
+ byHost: [],
299
+ diagnosis: "Network schema not exportable from this trace (likely recorded with a non-Network template).",
300
+ status: "not_present",
301
+ supportStatus: [
302
+ {
303
+ kind: "network-connections",
304
+ status: "not_exportable",
305
+ reason: "xctrace export failed; likely recorded with a non-Network template.",
306
+ },
307
+ ],
308
+ };
309
+ }
310
+ return analyzeNetworkActivityFromXml(result.stdout, tracePath, input.topN ?? 10, input.minBytes ?? 0);
311
+ }
312
+ //# sourceMappingURL=analyzeNetworkActivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeNetworkActivity.js","sourceRoot":"","sources":["../../src/tools/analyzeNetworkActivity.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import type { DataStatus } from "../types.js";
2
+ import type { DataStatus, SupportStatus } from "../types.js";
3
3
  export declare const analyzeTimeProfileSchema: z.ZodObject<{
4
4
  tracePath: z.ZodString;
5
5
  topN: z.ZodDefault<z.ZodNumber>;
@@ -19,6 +19,8 @@ export interface SampleEntry {
19
19
  weightFmt?: string;
20
20
  threadName?: string;
21
21
  symbol?: string;
22
+ /** Binary name for the leaf frame (e.g. "CoreFoundation", "libsystem_kernel.dylib"). Useful when the symbol is an unsymbolicated hex address. */
23
+ binary?: string;
22
24
  }
23
25
  export interface AnalyzeTimeProfileResult {
24
26
  ok: boolean;
@@ -34,6 +36,9 @@ export interface AnalyzeTimeProfileResult {
34
36
  /**
35
37
  * Optional notice explaining a known limitation
36
38
  * (e.g. xctrace crashed exporting the time-profile schema).
39
+ *
40
+ * @deprecated v1.14 item I. The same information now flows through
41
+ * `supportStatus[0].reason`. Kept for backwards compat.
37
42
  */
38
43
  notice?: string;
39
44
  diagnosis: string;
@@ -41,8 +46,12 @@ export interface AnalyzeTimeProfileResult {
41
46
  * Disambiguates empty arrays into "no data in the trace" vs "trace
42
47
  * could not be exported" vs "data was exported partially". Agents
43
48
  * should branch on this rather than `totalSamples === 0`.
49
+ *
50
+ * @deprecated v1.14 item I. Use `supportStatus[]` instead.
44
51
  */
45
52
  status: DataStatus;
53
+ /** v1.14+. Unified per-area status. See {@link SupportStatus}. */
54
+ supportStatus: SupportStatus[];
46
55
  }
47
56
  /**
48
57
  * Pure analysis from a chunk of xctrace XML. Aggregates sample counts per
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { existsSync } from "node:fs";
3
3
  import { resolve as resolvePath } from "node:path";
4
4
  import { runCommand } from "../runtime/exec.js";
5
+ import { fetchDiscoveredSchemas } from "../parsers/schemaDiscovery.js";
5
6
  import { parseXctraceXml, asNumber, asFormatted, } from "../parsers/xctraceXml.js";
6
7
  import { outputFormatField } from "../runtime/responseFormatter.js";
7
8
  export const analyzeTimeProfileSchema = z.object({
@@ -33,6 +34,13 @@ export function analyzeTimeProfileFromXml(xml, tracePath, topN = 20) {
33
34
  topRows: [],
34
35
  diagnosis: "No time-profile table found in the export.",
35
36
  status: "not_present",
37
+ supportStatus: [
38
+ {
39
+ kind: "time-profile",
40
+ status: "not_present",
41
+ reason: "Schema absent from the trace TOC.",
42
+ },
43
+ ],
36
44
  };
37
45
  }
38
46
  const rows = [];
@@ -40,14 +48,33 @@ export function analyzeTimeProfileFromXml(xml, tracePath, topN = 20) {
40
48
  for (const row of tp.rows) {
41
49
  const weight = asNumber(row.weight);
42
50
  const weightFmt = asFormatted(row.weight);
43
- // Symbol may live under 'backtrace' or 'symbol' or as a nested cell.
44
- const symbol = asFormatted(row.symbol) ??
45
- asFormatted(row["weight"]) ??
46
- row.backtrace?.fmt ??
47
- row.backtrace?.raw ??
48
- undefined;
51
+ // xctrace's time-profile schema names the backtrace column `stack`
52
+ // (mnemonic), with the underlying engineering-type `backtrace`. The
53
+ // parser keys cells by mnemonic, so the cell is at `row.stack`.
54
+ //
55
+ // The leaf frame's @_name attribute is the symbol when it could be
56
+ // resolved (e.g. `_CFRunLoopRunSpecificWithOptions`) or a raw hex
57
+ // address when unsymbolicated. In the unsymbolicated case we keep
58
+ // the binary name (e.g. `libsystem_kernel.dylib`) so aggregations
59
+ // still cluster by library instead of every sample being a unique
60
+ // address. Pre-2026-05-15 the parser only read `@_fmt` so this whole
61
+ // metadata was invisible and `topSymbols` was just the weight column
62
+ // text repeating "1.00 ms" for every sample.
63
+ const leafFrame = row.stack?.nested?.frame;
64
+ const binary = leafFrame?.nested?.binary?.name;
65
+ const frameName = leafFrame?.name;
66
+ // Real Apple traces expose the symbol on the leaf frame's @_name; some
67
+ // synthetic test fixtures use a dedicated <symbol> column instead. Try
68
+ // the stack first, fall back to the dedicated column.
69
+ const symbol = pickSymbol(frameName, binary) ?? asFormatted(row.symbol) ?? undefined;
49
70
  const threadName = row.thread?.fmt ?? undefined;
50
- rows.push({ weight, weightFmt, symbol, threadName });
71
+ rows.push({
72
+ weight,
73
+ weightFmt,
74
+ ...(symbol ? { symbol } : {}),
75
+ ...(binary ? { binary } : {}),
76
+ ...(threadName ? { threadName } : {}),
77
+ });
51
78
  if (symbol) {
52
79
  symbolCounts.set(symbol, (symbolCounts.get(symbol) ?? 0) + 1);
53
80
  }
@@ -69,8 +96,27 @@ export function analyzeTimeProfileFromXml(xml, tracePath, topN = 20) {
69
96
  ? "No samples found in the time-profile table."
70
97
  : `${rows.length} samples; top symbol: ${topSymbols[0]?.symbol ?? "unknown"} (${topSymbols[0]?.samples ?? 0} samples).`,
71
98
  status: "available",
99
+ supportStatus: [
100
+ {
101
+ kind: "time-profile",
102
+ status: "available",
103
+ sourceSchemas: ["time-profile"],
104
+ },
105
+ ],
72
106
  };
73
107
  }
108
+ /**
109
+ * Pick the most useful identifier for a sample given the leaf frame name +
110
+ * binary. Resolved symbol wins; otherwise we cluster by binary so the
111
+ * aggregation is still meaningful for unsymbolicated traces.
112
+ */
113
+ function pickSymbol(frameName, binary) {
114
+ if (frameName && !/^0x[0-9a-f]+$/i.test(frameName))
115
+ return frameName;
116
+ if (binary)
117
+ return frameName ? `${binary} (${frameName})` : binary;
118
+ return frameName;
119
+ }
74
120
  const SIGSEGV_NOTICE = `xctrace crashed exporting the time-profile schema (SIGSEGV). This is a known issue with heavy time-profile data and unsymbolicated traces.
75
121
 
76
122
  Workarounds:
@@ -82,13 +128,14 @@ export async function analyzeTimeProfile(input) {
82
128
  if (!existsSync(tracePath)) {
83
129
  throw new Error(`Trace bundle not found: ${tracePath}`);
84
130
  }
131
+ const { "time-profile": schemaName } = await fetchDiscoveredSchemas(runCommand, tracePath, ["time-profile"]);
85
132
  const result = await runCommand("xcrun", [
86
133
  "xctrace",
87
134
  "export",
88
135
  "--input",
89
136
  tracePath,
90
137
  "--xpath",
91
- '/trace-toc/run/data/table[@schema="time-profile"]',
138
+ `/trace-toc/run/data/table[@schema="${schemaName}"]`,
92
139
  ], { timeoutMs: 5 * 60_000 });
93
140
  if (result.code !== 0) {
94
141
  // SIGSEGV typically reports as 139 (128 + 11). Surface a useful message.
@@ -102,6 +149,14 @@ export async function analyzeTimeProfile(input) {
102
149
  notice: SIGSEGV_NOTICE,
103
150
  diagnosis: "Could not export time-profile schema (xctrace crashed). See `notice` for workarounds.",
104
151
  status: "not_exportable",
152
+ supportStatus: [
153
+ {
154
+ kind: "time-profile",
155
+ status: "not_exportable",
156
+ reason: SIGSEGV_NOTICE,
157
+ sourceSchemas: ["time-profile"],
158
+ },
159
+ ],
105
160
  };
106
161
  }
107
162
  throw new Error(`xctrace export failed (code ${result.code}): ${result.stderr || result.stdout}`);
@@ -1 +1 @@
1
- {"version":3,"file":"analyzeTimeProfile.js","sourceRoot":"","sources":["../../src/tools/analyzeTimeProfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,eAAe,EACf,QAAQ,EACR,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,qCAAqC,CAAC;IAClD,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,+DAA+D,CAAC;IAC5E,YAAY,EAAE,iBAAiB;CAChC,CAAC,CAAC;AAiCH;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAW,EACX,SAAiB,EACjB,IAAI,GAAG,EAAE;IAET,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO;YACL,EAAE,EAAE,IAAI;YACR,SAAS;YACT,YAAY,EAAE,CAAC;YACf,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,4CAA4C;YACvD,MAAM,EAAE,aAAa;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAkB,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,qEAAqE;QACrE,MAAM,MAAM,GACV,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;YACvB,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1B,GAAG,CAAC,SAAS,EAAE,GAAG;YAClB,GAAG,CAAC,SAAS,EAAE,GAAG;YAClB,SAAS,CAAC;QACZ,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,IAAI,MAAM,EAAE,CAAC;YACX,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;SAClD,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;SACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;SACrC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC;SACtB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;SACjD,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,OAAO;QACL,EAAE,EAAE,IAAI;QACR,SAAS;QACT,YAAY,EAAE,IAAI,CAAC,MAAM;QACzB,UAAU;QACV,OAAO;QACP,SAAS,EACP,IAAI,CAAC,MAAM,KAAK,CAAC;YACf,CAAC,CAAC,6CAA6C;YAC/C,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,yBAAyB,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,YAAY;QAC3H,MAAM,EAAE,WAAW;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,cAAc,GAAG;;;;;qHAK8F,CAAC;AAEtH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAA8B;IAE9B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,OAAO,EACP;QACE,SAAS;QACT,QAAQ;QACR,SAAS;QACT,SAAS;QACT,SAAS;QACT,mDAAmD;KACpD,EACD,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,EAAE,CAC1B,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,yEAAyE;QACzE,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/D,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,SAAS;gBACT,YAAY,EAAE,CAAC;gBACf,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,cAAc;gBACtB,SAAS,EACP,uFAAuF;gBACzF,MAAM,EAAE,gBAAgB;aACzB,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,yBAAyB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"analyzeTimeProfile.js","sourceRoot":"","sources":["../../src/tools/analyzeTimeProfile.ts"],"names":[],"mappings":"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"}
@@ -29,8 +29,8 @@ export declare const captureScenarioStateSchema: z.ZodEffects<z.ZodObject<{
29
29
  readonly include: z.ZodDefault<z.ZodArray<z.ZodEnum<["memgraph", "screenshot", "uiTree"]>, "many">>;
30
30
  }, "strip", z.ZodTypeAny, {
31
31
  outputDir: string;
32
- simulatorUDID: string;
33
32
  label: string;
33
+ simulatorUDID: string;
34
34
  include: ("screenshot" | "memgraph" | "uiTree")[];
35
35
  pid?: number | undefined;
36
36
  appName?: string | undefined;
@@ -43,8 +43,8 @@ export declare const captureScenarioStateSchema: z.ZodEffects<z.ZodObject<{
43
43
  include?: ("screenshot" | "memgraph" | "uiTree")[] | undefined;
44
44
  }>, {
45
45
  outputDir: string;
46
- simulatorUDID: string;
47
46
  label: string;
47
+ simulatorUDID: string;
48
48
  include: ("screenshot" | "memgraph" | "uiTree")[];
49
49
  pid?: number | undefined;
50
50
  appName?: string | undefined;
@@ -5,16 +5,19 @@ export declare const countAliveSchema: z.ZodObject<{
5
5
  className: z.ZodOptional<z.ZodString>;
6
6
  topN: z.ZodDefault<z.ZodNumber>;
7
7
  includeReferenceTree: z.ZodDefault<z.ZodBoolean>;
8
+ sortBy: z.ZodDefault<z.ZodEnum<["count", "totalBytes"]>>;
8
9
  }, "strip", z.ZodTypeAny, {
9
10
  path: string;
10
11
  topN: number;
11
12
  includeReferenceTree: boolean;
13
+ sortBy: "count" | "totalBytes";
12
14
  className?: string | undefined;
13
15
  }, {
14
16
  path: string;
15
17
  className?: string | undefined;
16
18
  topN?: number | undefined;
17
19
  includeReferenceTree?: boolean | undefined;
20
+ sortBy?: "count" | "totalBytes" | undefined;
18
21
  }>;
19
22
  export type CountAliveInput = z.infer<typeof countAliveSchema>;
20
23
  export interface CountAliveEntry {
@@ -24,6 +27,22 @@ export interface CountAliveEntry {
24
27
  byCycle?: number;
25
28
  /** When `includeReferenceTree: true`, the reference-tree contribution. Often the only non-zero side on `leakCount: 0` memgraphs. */
26
29
  byReferenceTree?: number;
30
+ /**
31
+ * v1.14+. Per-instance size in bytes derived from the memgraph. For
32
+ * fixed-size ObjC classes every instance has the same size; for
33
+ * variable-size classes (NSData with payload, etc.) this is an average
34
+ * (totalBytes / instanceCount, rounded). Absent when neither the
35
+ * cycle-side `[N]` annotation nor the reference-tree parens-size
36
+ * carried a number (rare).
37
+ */
38
+ instanceSizeBytes?: number;
39
+ /**
40
+ * v1.14+. Total bytes attributed to this class: sum of per-instance
41
+ * sizes from the cycle forest plus the reference-tree totals. FLEX
42
+ * surfaces this as the "Size" sort column in its Live Objects view.
43
+ * Useful for "where is my memory going?" investigations.
44
+ */
45
+ totalBytes?: number;
27
46
  }
28
47
  export interface CountAliveResult {
29
48
  ok: boolean;
@@ -41,6 +60,21 @@ export interface CountAliveResult {
41
60
  */
42
61
  actionableCounts?: CountAliveEntry[];
43
62
  }
44
- /** Pure: count node occurrences by exact className across the cycle forest. */
63
+ /** Pure: count node occurrences by exact className across the cycle forest.
64
+ * Backwards-compatible API; for v1.14 byte aggregation use
65
+ * {@link countByClassWithBytes}. */
45
66
  export declare function countByClass(report: LeaksReport): Map<string, number>;
67
+ /**
68
+ * Pure: aggregate occurrences AND bytes by exact className across the
69
+ * cycle forest. Each node's `instanceSize` is summed into `totalBytes`;
70
+ * the first non-zero `instanceSize` seen is recorded as the canonical
71
+ * `instanceSizeBytes` (ObjC classes are typically fixed-size, so the
72
+ * first value is representative). Nodes without a size annotation
73
+ * contribute to count but not bytes. v1.14.
74
+ */
75
+ export declare function countByClassWithBytes(report: LeaksReport): Map<string, {
76
+ count: number;
77
+ totalBytes: number;
78
+ instanceSizeBytes?: number;
79
+ }>;
46
80
  export declare function countAlive(input: CountAliveInput): Promise<CountAliveResult>;