Analyze cold/warm launch breakdown
analyzeAppLaunchParse app launch trace files to extract total launch time, launch type, per-phase breakdown, and identify the slowest phase.
Instructions
[mg.trace] Parse the app-launch schema from a .trace recorded with the App Launch Instruments template. Returns total launch time, launch type (cold/warm), per-phase breakdown (process-creation, dyld-init, ObjC-init, AppDelegate, first-frame), and the slowest phase.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tracePath | Yes | Absolute path to a `.trace` bundle recorded with the App Launch template (`xcrun xctrace record --template 'App Launch' --launch <bundleId>`). |
Implementation Reference
- src/tools/analyzeAppLaunch.ts:165-190 (handler)Main handler function that resolves the trace path, runs `xcrun xctrace export` to extract the app-launch table, and delegates to analyzeAppLaunchFromXml for parsing.
export async function analyzeAppLaunch( input: AnalyzeAppLaunchInput, ): Promise<AnalyzeAppLaunchResult> { const tracePath = resolvePath(input.tracePath); if (!existsSync(tracePath)) { throw new Error(`Trace bundle not found: ${tracePath}`); } const result = await runCommand( "xcrun", [ "xctrace", "export", "--input", tracePath, "--xpath", '/trace-toc/run/data/table[@schema="app-launch"]', ], { timeoutMs: 5 * 60_000 }, ); if (result.code !== 0) { throw new Error( `xctrace export failed (code ${result.code}): ${result.stderr || result.stdout}`, ); } return analyzeAppLaunchFromXml(result.stdout, tracePath); } - src/tools/analyzeAppLaunch.ts:58-137 (handler)Pure function that parses xctrace XML, extracts total duration, launch type (cold/warm/unknown), per-phase breakdown sorted by canonical order, identifies the slowest phase, and builds a diagnosis string.
export function analyzeAppLaunchFromXml( xml: string, tracePath: string, ): AnalyzeAppLaunchResult { const tables = parseXctraceXml(xml); const table = tables.find((t) => t.schema === "app-launch"); if (!table) { return { ok: true, tracePath, totalLaunchMs: 0, launchType: "unknown", phases: [], diagnosis: "No app-launch table found in the trace.", }; } // The app-launch schema can include rows describing per-phase durations and // a summary row with the total. xctrace varies the field shape across iOS // versions, so we cope with whichever fields turn up. const rawPhases: Array<{ phase: string; label: string; durationNs: number }> = []; let totalNs = 0; let launchType: AnalyzeAppLaunchResult["launchType"] = "unknown"; for (const row of table.rows) { const phase = asFormatted(row.phase) ?? asFormatted(row["phase-name"]) ?? asFormatted(row.category) ?? "unknown"; const label = asFormatted(row["display-label"]) ?? asFormatted(row.label) ?? phase; const dn = asNumber(row.duration) ?? asNumber(row["phase-duration"]) ?? 0; if (phase === "total" || phase === "launch-total") { totalNs = dn; const t = asFormatted(row["launch-type"]); if (t === "cold" || t === "warm") launchType = t; continue; } if (dn === 0) continue; rawPhases.push({ phase, label, durationNs: dn }); } // If no explicit total row, sum the phases. if (totalNs === 0) { totalNs = rawPhases.reduce((sum, p) => sum + p.durationNs, 0); } const totalMs = totalNs / 1_000_000; const phases: PhaseEntry[] = rawPhases .map((p) => ({ phase: p.phase, label: p.label, durationMs: p.durationNs / 1_000_000, percentOfTotal: totalNs > 0 ? (p.durationNs / totalNs) * 100 : 0, })) .sort((a, b) => phaseOrder(a.phase) - phaseOrder(b.phase)); const slowestPhase = phases.length === 0 ? undefined : [...phases].sort((a, b) => b.durationMs - a.durationMs)[0]; const diagnosis = buildDiagnosis(totalMs, launchType, slowestPhase); return { ok: true, tracePath, totalLaunchMs: totalMs, launchType, phases, slowestPhase, diagnosis, }; } - src/tools/analyzeAppLaunch.ts:11-18 (schema)Zod schema for the tool input: expects a single required `tracePath` (string, absolute path to a .trace bundle).
export const analyzeAppLaunchSchema = z.object({ tracePath: z .string() .min(1) .describe( "Absolute path to a `.trace` bundle recorded with the App Launch template (`xcrun xctrace record --template 'App Launch' --launch <bundleId>`).", ), }); - src/tools/analyzeAppLaunch.ts:43-55 (schema)TypeScript interface for the output result: ok, tracePath, totalLaunchMs, launchType (cold/warm/unknown), phases (PhaseEntry[]), slowestPhase, and diagnosis.
export interface AnalyzeAppLaunchResult { ok: boolean; tracePath: string; /** Total app launch time as reported by xctrace. */ totalLaunchMs: number; /** "cold" or "warm" launch when discriminable; otherwise "unknown". */ launchType: "cold" | "warm" | "unknown"; /** Per-phase breakdown sorted by Apple's canonical order. */ phases: PhaseEntry[]; /** Phase that took the largest share of launch time. */ slowestPhase?: PhaseEntry; diagnosis: string; } - src/index.ts:341-353 (registration)MCP server registration: registers 'analyzeAppLaunch' tool with title, description, inputSchema, and a handler that calls analyzeAppLaunch(input) and stringifies the result.
server.registerTool( "analyzeAppLaunch", { title: "Analyze cold/warm launch breakdown", description: "[mg.trace] Parse the `app-launch` schema from a `.trace` recorded with the App Launch Instruments template. Returns total launch time, launch type (cold/warm), per-phase breakdown (process-creation, dyld-init, ObjC-init, AppDelegate, first-frame), and the slowest phase.", inputSchema: analyzeAppLaunchSchema.shape, }, async (input) => { const result = await analyzeAppLaunch(input); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, );