@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 +21 -0
- package/README.md +482 -0
- package/dist/analyzer/comparative-analyzer.d.ts +38 -0
- package/dist/analyzer/comparative-analyzer.d.ts.map +1 -0
- package/dist/analyzer/comparative-analyzer.js +255 -0
- package/dist/analyzer/comparative-analyzer.js.map +1 -0
- package/dist/analyzer/performance-analyzer.d.ts +49 -0
- package/dist/analyzer/performance-analyzer.d.ts.map +1 -0
- package/dist/analyzer/performance-analyzer.js +413 -0
- package/dist/analyzer/performance-analyzer.js.map +1 -0
- package/dist/analyzer/recommendation-engine.d.ts +39 -0
- package/dist/analyzer/recommendation-engine.d.ts.map +1 -0
- package/dist/analyzer/recommendation-engine.js +306 -0
- package/dist/analyzer/recommendation-engine.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/trace-parser.d.ts +154 -0
- package/dist/parser/trace-parser.d.ts.map +1 -0
- package/dist/parser/trace-parser.js +1738 -0
- package/dist/parser/trace-parser.js.map +1 -0
- package/dist/types.d.ts +371 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/xctrace-runner.d.ts +81 -0
- package/dist/utils/xctrace-runner.d.ts.map +1 -0
- package/dist/utils/xctrace-runner.js +420 -0
- package/dist/utils/xctrace-runner.js.map +1 -0
- package/package.json +60 -0
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"}
|