@xctrace-analyzer/core 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 James Rochabrun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,482 @@
1
+ # @xctrace-analyzer/core
2
+
3
+ > Core analysis library for Xcode Instruments performance traces
4
+
5
+ A TypeScript library for parsing and analyzing Xcode Instruments `.trace` files, identifying Time Profiler bottlenecks, main-thread hangs, app-attributed user-code frames, and additional Memory, Network, Energy, Allocations, and Leaks findings.
6
+
7
+ ## Features
8
+
9
+ - ๐Ÿ“Š Parse Time Profiler traces from xctrace XML output
10
+ - ๐Ÿงญ Parse Memory, Network, Energy, Allocations, and Leaks tables when exportable
11
+ - ๐Ÿ” Identify slow functions and performance bottlenecks
12
+ - ๐ŸงŠ Surface main-thread hang events without treating clean CPU thresholds as a global all-clear
13
+ - ๐ŸŽฏ Attribute Time Profiler samples to Top User-Code Frames
14
+ - ๐Ÿ“ˆ Compare traces to detect performance regressions
15
+ - ๐Ÿ’ก Generate actionable optimization recommendations
16
+ - ๐ŸŽฏ Pattern-based suggestion engine (image caching, async operations, etc.)
17
+ - ๐Ÿงพ Support status and export diagnostics for honest reporting
18
+
19
+ ## What The Core Package Does
20
+
21
+ The core package is the reusable analysis layer behind the MCP server. It does not speak MCP directly. It provides:
22
+
23
+ - `xctrace` process utilities for recording, exporting TOCs, exporting XPath tables, and exporting HAR data
24
+ - Capability detection and symbolication utilities for local `xctrace`
25
+ - `TraceParser` for normalizing Xcode `.trace` XML/HAR data into typed TypeScript structures
26
+ - `PerformanceAnalyzer` for Time Profiler statistics, bottlenecks, main-thread hang callouts, user-code frame attribution, and summaries
27
+ - `RecommendationEngine` for CPU, memory, network, allocation, leak, and energy recommendations
28
+ - `ComparativeAnalyzer` for Time Profiler baseline/current regression checks
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @xctrace-analyzer/core
34
+ ```
35
+
36
+ For source development, run `pnpm install --frozen-lockfile` and `pnpm build` from the monorepo root.
37
+
38
+ ## Quick Start
39
+
40
+ ```typescript
41
+ import { analyzeTraceFile, compareTraceFiles } from '@xctrace-analyzer/core';
42
+
43
+ // Analyze a single trace
44
+ const analysis = await analyzeTraceFile('/path/to/app.trace', {
45
+ slowThreshold: 100, // ms
46
+ topN: 10,
47
+ timeRangeMs: { startMs: 2000, endMs: 7000 },
48
+ userBinaryHints: ['MyApp'],
49
+ });
50
+
51
+ console.log(analysis.summary);
52
+ console.log('Bottlenecks:', analysis.bottlenecks);
53
+ console.log('User-code frames:', analysis.userFrameProfiles);
54
+ console.log('Recommendations:', analysis.recommendations);
55
+
56
+ // Compare two traces
57
+ const comparison = await compareTraceFiles(
58
+ '/path/to/baseline.trace',
59
+ '/path/to/current.trace',
60
+ undefined, // analysis options
61
+ { regressionThreshold: 10 } // comparison options
62
+ );
63
+
64
+ console.log(comparison.summary);
65
+ console.log('Regressions:', comparison.regressions);
66
+ console.log('Improvements:', comparison.improvements);
67
+ ```
68
+
69
+ ## API Reference
70
+
71
+ ### High-Level Functions
72
+
73
+ #### `analyzeTraceFile(tracePath, options?)`
74
+
75
+ Parse and analyze a trace file in one call.
76
+
77
+ **Returns:** `Promise<Analysis>`
78
+
79
+ **Options:**
80
+ ```typescript
81
+ interface AnalysisOptions {
82
+ slowThreshold?: number; // ms threshold for slow functions (default: 100)
83
+ topN?: number; // show top N functions (default: 10)
84
+ includeRecommendations?: boolean; // generate recommendations (default: true)
85
+ minCallCount?: number; // minimum calls to consider (default: 1)
86
+ timeRangeMs?: { startMs: number; endMs: number }; // trace-relative window
87
+ userBinaryHints?: string[]; // app/module names for user-code attribution
88
+ }
89
+ ```
90
+
91
+ #### `compareTraceFiles(baselinePath, currentPath, analysisOptions?, comparisonOptions?)`
92
+
93
+ Compare two trace files for regressions and improvements.
94
+
95
+ **Returns:** `Promise<Comparison>`
96
+
97
+ **Comparison Options:**
98
+ ```typescript
99
+ interface ComparisonOptions {
100
+ failOnRegression?: boolean; // reserved for MCP/CLI automation (default: false)
101
+ regressionThreshold?: number; // % increase to flag (default: 10)
102
+ minDuration?: number; // only compare functions > N ms (default: 10)
103
+ }
104
+ ```
105
+
106
+ ### Core Classes
107
+
108
+ #### `TraceParser`
109
+
110
+ Parses xctrace XML output into structured data.
111
+
112
+ ```typescript
113
+ import { TraceParser } from '@xctrace-analyzer/core';
114
+
115
+ const parser = new TraceParser();
116
+ const parsed = await parser.parseTrace('/path/to/app.trace');
117
+ ```
118
+
119
+ #### `PerformanceAnalyzer`
120
+
121
+ Analyzes parsed traces for bottlenecks.
122
+
123
+ ```typescript
124
+ import { PerformanceAnalyzer } from '@xctrace-analyzer/core';
125
+
126
+ const analyzer = new PerformanceAnalyzer();
127
+ const analysis = analyzer.analyze(parsedTrace, {
128
+ slowThreshold: 100,
129
+ topN: 10,
130
+ });
131
+ ```
132
+
133
+ #### `ComparativeAnalyzer`
134
+
135
+ Compares two analyses to detect regressions.
136
+
137
+ ```typescript
138
+ import { ComparativeAnalyzer } from '@xctrace-analyzer/core';
139
+
140
+ const comparator = new ComparativeAnalyzer();
141
+ const comparison = comparator.compare(baselineAnalysis, currentAnalysis, {
142
+ regressionThreshold: 10,
143
+ });
144
+ ```
145
+
146
+ #### `RecommendationEngine`
147
+
148
+ Generates optimization recommendations.
149
+
150
+ ```typescript
151
+ import { RecommendationEngine } from '@xctrace-analyzer/core';
152
+
153
+ const engine = new RecommendationEngine();
154
+ const recommendations = engine.generateRecommendations(analysis);
155
+ ```
156
+
157
+ ### Utilities
158
+
159
+ #### xctrace-runner
160
+
161
+ Utility functions for calling xctrace commands.
162
+
163
+ ```typescript
164
+ import {
165
+ isXCTraceAvailable,
166
+ getXCTraceVersion,
167
+ exportTOC,
168
+ exportTable,
169
+ exportHAR,
170
+ listTemplates,
171
+ listDevices,
172
+ getXCTraceCapabilities,
173
+ recordTrace,
174
+ symbolicateTrace,
175
+ } from '@xctrace-analyzer/core';
176
+
177
+ // Check availability
178
+ const available = await isXCTraceAvailable();
179
+
180
+ // List templates
181
+ const templates = await listTemplates();
182
+
183
+ // Export time profile data
184
+ const xml = await exportTable('/path/to/trace.trace', 'time-profile');
185
+
186
+ // Export network HAR data when available
187
+ const har = await exportHAR('/path/to/network.trace');
188
+
189
+ // Inspect local xctrace capabilities
190
+ const capabilities = await getXCTraceCapabilities();
191
+
192
+ // Record a running app with the Leaks template
193
+ await recordTrace({
194
+ template: 'Leaks',
195
+ processName: 'MyApp',
196
+ duration: 60,
197
+ outputPath: '/path/to/MyApp-leaks.trace',
198
+ });
199
+
200
+ // Symbolicate to a separate output trace before analysis
201
+ await symbolicateTrace({
202
+ inputPath: '/path/to/app.trace',
203
+ outputPath: '/path/to/app-symbolicated.trace',
204
+ dsymPath: '/path/to/App.dSYM',
205
+ });
206
+
207
+ // Record one combined profiling trace
208
+ await recordTrace({
209
+ template: 'Time Profiler',
210
+ instruments: ['Leaks', 'Allocations', 'HTTP Traffic'],
211
+ processName: 'MyApp',
212
+ duration: 60,
213
+ outputPath: '/path/to/MyApp-full.trace',
214
+ });
215
+ ```
216
+
217
+ `recordTrace` intentionally uses `execFile` argument arrays rather than shell command strings. This keeps paths and process names safe even when they contain spaces.
218
+
219
+ ## Data Types
220
+
221
+ ### Analysis
222
+
223
+ Complete analysis result for a trace.
224
+
225
+ ```typescript
226
+ interface Analysis {
227
+ metadata: TraceMetadata;
228
+ stats: PerformanceStats;
229
+ bottlenecks: Bottleneck[];
230
+ recommendations: Recommendation[];
231
+ topFunctions: FunctionProfile[];
232
+ userFrameProfiles?: UserFrameProfile[];
233
+ instrumentAnalyses: InstrumentAnalysis[];
234
+ hangs?: HangsData;
235
+ supportStatus?: AnalysisSupportStatus[];
236
+ exportAttempts?: ExportAttempt[];
237
+ summary: string;
238
+ }
239
+ ```
240
+
241
+ ### UserFrameProfile
242
+
243
+ App-attributed Time Profiler frame. The analyzer walks each sample backtrace from leaf to root and aggregates the deepest frame whose module matches TOC-derived user process names or `userBinaryHints`.
244
+
245
+ ```typescript
246
+ interface UserFrameProfile {
247
+ name: string;
248
+ module?: string;
249
+ selfTime: number;
250
+ sampleCount: number;
251
+ percentage: number;
252
+ }
253
+ ```
254
+
255
+ ### InstrumentAnalysis
256
+
257
+ Normalized analysis for non-Time-Profiler instruments.
258
+
259
+ ```typescript
260
+ interface InstrumentAnalysis {
261
+ kind: 'time-profile' | 'memory' | 'network' | 'energy' | 'allocations' | 'leaks';
262
+ title: string;
263
+ summary: string;
264
+ metrics: InstrumentMetric[];
265
+ findings: InstrumentFinding[];
266
+ sourceSchemas: string[];
267
+ }
268
+ ```
269
+
270
+ ### Comparison
271
+
272
+ Result of comparing two traces.
273
+
274
+ ```typescript
275
+ interface Comparison {
276
+ baseline: Analysis;
277
+ current: Analysis;
278
+ delta: PerformanceDelta;
279
+ regressions: Regression[];
280
+ improvements: Improvement[];
281
+ hasRegression: boolean;
282
+ hasCriticalRegression: boolean;
283
+ summary: string;
284
+ }
285
+ ```
286
+
287
+ ### Bottleneck
288
+
289
+ A performance bottleneck identified in the trace.
290
+
291
+ ```typescript
292
+ interface Bottleneck {
293
+ function: string;
294
+ module?: string;
295
+ impact: 'critical' | 'high' | 'medium' | 'low';
296
+ duration: number; // milliseconds
297
+ percentage: number; // % of total time
298
+ suggestion: string;
299
+ callCount?: number;
300
+ }
301
+ ```
302
+
303
+ ### Recommendation
304
+
305
+ An actionable optimization recommendation.
306
+
307
+ ```typescript
308
+ interface Recommendation {
309
+ type: 'optimization' | 'architecture' | 'caching' | 'async' | 'memory' | 'algorithm';
310
+ priority: 'high' | 'medium' | 'low';
311
+ title: string;
312
+ description: string;
313
+ affectedFunctions: string[];
314
+ potentialImprovement: string;
315
+ codeExample?: string; // Swift code example
316
+ }
317
+ ```
318
+
319
+ ## Examples
320
+
321
+ ### Basic Analysis
322
+
323
+ ```typescript
324
+ import { analyzeTraceFile } from '@xctrace-analyzer/core';
325
+
326
+ async function analyzeApp() {
327
+ const analysis = await analyzeTraceFile('./app.trace', {
328
+ slowThreshold: 100,
329
+ topN: 5,
330
+ });
331
+
332
+ console.log(`\n๐Ÿ“Š Analysis Summary:`);
333
+ console.log(analysis.summary);
334
+
335
+ console.log(`\n๐ŸŒ Slow Functions:`);
336
+ for (const bottleneck of analysis.bottlenecks.slice(0, 3)) {
337
+ console.log(`- ${bottleneck.function}: ${bottleneck.duration.toFixed(0)}ms`);
338
+ console.log(` ๐Ÿ’ก ${bottleneck.suggestion}`);
339
+ }
340
+
341
+ console.log(`\n๐Ÿ’ก Top Recommendations:`);
342
+ for (const rec of analysis.recommendations.slice(0, 3)) {
343
+ console.log(`\n${rec.title} (${rec.priority} priority)`);
344
+ console.log(rec.description);
345
+ console.log(`Potential improvement: ${rec.potentialImprovement}`);
346
+ }
347
+ }
348
+
349
+ analyzeApp().catch(console.error);
350
+ ```
351
+
352
+ ### Regression Detection
353
+
354
+ ```typescript
355
+ import { compareTraceFiles } from '@xctrace-analyzer/core';
356
+
357
+ async function checkRegression() {
358
+ try {
359
+ const comparison = await compareTraceFiles(
360
+ './baseline.trace',
361
+ './current.trace',
362
+ undefined,
363
+ {
364
+ regressionThreshold: 15, // fail if >15% slower
365
+ }
366
+ );
367
+
368
+ console.log(comparison.summary);
369
+
370
+ if (comparison.regressions.length > 0) {
371
+ console.log('\nโš ๏ธ Regressions found:');
372
+ for (const reg of comparison.regressions) {
373
+ console.log(
374
+ `- ${reg.function}: ` +
375
+ `${reg.baselineTime.toFixed(0)}ms โ†’ ${reg.currentTime.toFixed(0)}ms ` +
376
+ `(+${reg.percentageIncrease.toFixed(0)}%)`
377
+ );
378
+ }
379
+ process.exit(1); // Fail CI
380
+ }
381
+
382
+ console.log('\nโœ… No significant regressions');
383
+ } catch (error) {
384
+ console.error('Regression detected!', error);
385
+ process.exit(1);
386
+ }
387
+ }
388
+
389
+ checkRegression();
390
+ ```
391
+
392
+ ### Custom Analysis Pipeline
393
+
394
+ ```typescript
395
+ import {
396
+ TraceParser,
397
+ PerformanceAnalyzer,
398
+ RecommendationEngine,
399
+ } from '@xctrace-analyzer/core';
400
+
401
+ async function customAnalysis() {
402
+ // Step 1: Parse
403
+ const parser = new TraceParser();
404
+ const trace = await parser.parseTrace('./app.trace');
405
+
406
+ console.log(`Parsed ${trace.metadata.fileName}`);
407
+ console.log(`Duration: ${trace.metadata.duration}ms`);
408
+
409
+ // Step 2: Analyze
410
+ const analyzer = new PerformanceAnalyzer();
411
+ const analysis = analyzer.analyze(trace, {
412
+ slowThreshold: 50, // Lower threshold
413
+ topN: 20, // More functions
414
+ });
415
+
416
+ // Step 3: Generate recommendations
417
+ const engine = new RecommendationEngine();
418
+ analysis.recommendations = engine.generateRecommendations(analysis);
419
+
420
+ // Step 4: Custom reporting
421
+ const criticalBottlenecks = analysis.bottlenecks.filter(
422
+ b => b.impact === 'critical'
423
+ );
424
+
425
+ if (criticalBottlenecks.length > 0) {
426
+ console.error('๐Ÿ”ด Critical performance issues found!');
427
+ for (const b of criticalBottlenecks) {
428
+ console.error(`- ${b.function}: ${b.duration}ms`);
429
+ }
430
+ }
431
+
432
+ return analysis;
433
+ }
434
+
435
+ customAnalysis();
436
+ ```
437
+
438
+ ## Requirements
439
+
440
+ - **macOS** with Xcode Command Line Tools installed
441
+ - **Node.js** 18+
442
+ - **xctrace** command-line tool (included with Xcode)
443
+ - Time Profiler, Memory, Network, Energy, Allocations, or Leaks `.trace` files. Availability depends on which tables `xcrun xctrace export --toc` exposes for the trace.
444
+
445
+ ## Error Handling
446
+
447
+ The library provides specific error types:
448
+
449
+ ```typescript
450
+ import {
451
+ TraceParserError,
452
+ XCTraceError,
453
+ AnalysisError,
454
+ } from '@xctrace-analyzer/core';
455
+
456
+ try {
457
+ const analysis = await analyzeTraceFile('./nonexistent.trace');
458
+ } catch (error) {
459
+ if (error instanceof TraceParserError) {
460
+ console.error('Failed to parse trace:', error.message);
461
+ } else if (error instanceof XCTraceError) {
462
+ console.error('xctrace command failed:', error.message);
463
+ console.error('Exit code:', error.exitCode);
464
+ } else if (error instanceof AnalysisError) {
465
+ console.error('Analysis failed:', error.message);
466
+ }
467
+ }
468
+ ```
469
+
470
+ ## Production Notes
471
+
472
+ - This package analyzes data that `xcrun xctrace export` exposes through TOC, XPath, and HAR exports.
473
+ - It reports data that is not present in the trace or not exportable explicitly instead of assuming Instruments.app GUI parity.
474
+ - Large-trace streaming and broader template coverage are tracked as production hardening work, not current guarantees.
475
+
476
+ ## License
477
+
478
+ MIT
479
+
480
+ ## Contributing
481
+
482
+ See the main repository README for contribution guidelines.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ComparativeAnalyzer - Compares two traces and detects regressions
3
+ */
4
+ import { Analysis, Comparison, ComparisonOptions } from '../types.js';
5
+ /**
6
+ * Compares two trace analyses
7
+ */
8
+ export declare class ComparativeAnalyzer {
9
+ /**
10
+ * Compare two analyses (baseline vs current)
11
+ */
12
+ compare(baseline: Analysis, current: Analysis, options?: ComparisonOptions): Comparison;
13
+ /**
14
+ * Calculate performance delta between two analyses
15
+ */
16
+ private calculateDelta;
17
+ /**
18
+ * Detect performance regressions
19
+ */
20
+ private detectRegressions;
21
+ /**
22
+ * Detect performance improvements
23
+ */
24
+ private detectImprovements;
25
+ /**
26
+ * Determine regression severity
27
+ */
28
+ private determineRegressionSeverity;
29
+ /**
30
+ * Generate comparison summary
31
+ */
32
+ private generateComparisonSummary;
33
+ }
34
+ /**
35
+ * Convenience function to compare traces
36
+ */
37
+ export declare function compareTraces(baseline: Analysis, current: Analysis, options?: ComparisonOptions): Comparison;
38
+ //# sourceMappingURL=comparative-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparative-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/comparative-analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,QAAQ,EACR,UAAU,EACV,iBAAiB,EAMlB,MAAM,aAAa,CAAC;AAWrB;;GAEG;AACH,qBAAa,mBAAmB;IAC9B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAsB,GAAG,UAAU;IAmC3F;;OAEG;IACH,OAAO,CAAC,cAAc;IA8CtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAyEzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAsD1B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA4BnC;;OAEG;IACH,OAAO,CAAC,yBAAyB;CAmDlC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CAGZ"}