memorydetective 1.13.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 (44) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +6 -2
  3. package/USAGE.md +29 -0
  4. package/dist/index.js +9 -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/tools/analyzeAllocations.d.ts +5 -1
  13. package/dist/tools/analyzeAllocations.js +17 -1
  14. package/dist/tools/analyzeAllocations.js.map +1 -1
  15. package/dist/tools/analyzeAnimationHitches.d.ts +5 -1
  16. package/dist/tools/analyzeAnimationHitches.js +17 -1
  17. package/dist/tools/analyzeAnimationHitches.js.map +1 -1
  18. package/dist/tools/analyzeAppLaunch.d.ts +3 -0
  19. package/dist/tools/analyzeAppLaunch.js +17 -1
  20. package/dist/tools/analyzeAppLaunch.js.map +1 -1
  21. package/dist/tools/analyzeHangs.d.ts +63 -3
  22. package/dist/tools/analyzeHangs.js +143 -19
  23. package/dist/tools/analyzeHangs.js.map +1 -1
  24. package/dist/tools/analyzeNetworkActivity.d.ts +99 -0
  25. package/dist/tools/analyzeNetworkActivity.js +312 -0
  26. package/dist/tools/analyzeNetworkActivity.js.map +1 -0
  27. package/dist/tools/analyzeTimeProfile.d.ts +10 -1
  28. package/dist/tools/analyzeTimeProfile.js +63 -8
  29. package/dist/tools/analyzeTimeProfile.js.map +1 -1
  30. package/dist/tools/countAlive.d.ts +35 -1
  31. package/dist/tools/countAlive.js +124 -29
  32. package/dist/tools/countAlive.js.map +1 -1
  33. package/dist/tools/inspectTrace.js +112 -18
  34. package/dist/tools/inspectTrace.js.map +1 -1
  35. package/dist/tools/recordTimeProfile.d.ts +83 -0
  36. package/dist/tools/recordTimeProfile.js +135 -0
  37. package/dist/tools/recordTimeProfile.js.map +1 -1
  38. package/dist/tools/replayScenario.d.ts +4 -4
  39. package/dist/tools/summarizeTrace.d.ts +2 -2
  40. package/dist/tools/verifyFix.d.ts +27 -0
  41. package/dist/tools/verifyFix.js +78 -4
  42. package/dist/tools/verifyFix.js.map +1 -1
  43. package/dist/types.d.ts +28 -0
  44. package/package.json +2 -2
@@ -0,0 +1,99 @@
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 type { DataStatus, SupportStatus } from "../types.js";
25
+ export declare const analyzeNetworkActivitySchema: z.ZodObject<{
26
+ tracePath: z.ZodString;
27
+ topN: z.ZodDefault<z.ZodNumber>;
28
+ minBytes: z.ZodDefault<z.ZodNumber>;
29
+ outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ tracePath: string;
32
+ topN: number;
33
+ minBytes: number;
34
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
35
+ }, {
36
+ tracePath: string;
37
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
38
+ topN?: number | undefined;
39
+ minBytes?: number | undefined;
40
+ }>;
41
+ export type AnalyzeNetworkActivityInput = z.infer<typeof analyzeNetworkActivitySchema>;
42
+ export interface NetworkConnectionEntry {
43
+ /** Start timestamp in nanoseconds since recording start. */
44
+ startNs: number;
45
+ startFmt?: string;
46
+ /** Response/transaction duration in nanoseconds when available. */
47
+ durationNs?: number;
48
+ durationMs?: number;
49
+ durationFmt?: string;
50
+ /** URL or hostname (whichever the trace exposed). */
51
+ url?: string;
52
+ /** Host portion of the URL, when parseable. */
53
+ host?: string;
54
+ /** HTTP method (GET, POST, etc.) when present. */
55
+ method?: string;
56
+ /** HTTP response status code. */
57
+ statusCode?: number;
58
+ /** Bytes received from the server (response body + headers). */
59
+ bytesIn?: number;
60
+ /** Bytes sent to the server (request body + headers). */
61
+ bytesOut?: number;
62
+ }
63
+ export interface NetworkHostAggregate {
64
+ host: string;
65
+ count: number;
66
+ bytesIn: number;
67
+ bytesOut: number;
68
+ longestMs: number;
69
+ }
70
+ export interface AnalyzeNetworkActivityResult {
71
+ ok: boolean;
72
+ tracePath: string;
73
+ totals: {
74
+ rows: number;
75
+ totalBytesIn: number;
76
+ totalBytesOut: number;
77
+ longestMs: number;
78
+ averageMs: number;
79
+ /** Status-code bucket counts. Example: `{ "2xx": 47, "4xx": 3, "5xx": 1, "n/a": 12 }`. */
80
+ statusBuckets: Record<string, number>;
81
+ };
82
+ /** Top N connections ranked by `durationMs` desc. */
83
+ topByDuration: NetworkConnectionEntry[];
84
+ /** Top N connections ranked by `bytesIn + bytesOut` desc. */
85
+ topByBytes: NetworkConnectionEntry[];
86
+ /** Per-host aggregates, ranked by request count desc. */
87
+ byHost: NetworkHostAggregate[];
88
+ diagnosis: string;
89
+ /** @deprecated v1.14 item I. Use `supportStatus[]` instead. */
90
+ status: DataStatus;
91
+ /** v1.14+. Unified per-area status. See {@link SupportStatus}. */
92
+ supportStatus: SupportStatus[];
93
+ }
94
+ /** Extract a host string from a URL-or-host value. Falls back to the
95
+ * raw input when it does not look like a URL (already a host). */
96
+ export declare function extractHost(urlOrHost: string | undefined): string | undefined;
97
+ /** Pure: turn the network-connections XML into the analyzed result. */
98
+ export declare function analyzeNetworkActivityFromXml(xml: string, tracePath: string, topN?: number, minBytes?: number): AnalyzeNetworkActivityResult;
99
+ export declare function analyzeNetworkActivity(input: AnalyzeNetworkActivityInput): Promise<AnalyzeNetworkActivityResult>;
@@ -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":"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"}
1
+ {"version":3,"file":"analyzeTimeProfile.js","sourceRoot":"","sources":["../../src/tools/analyzeTimeProfile.ts"],"names":[],"mappings":"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"}