Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Reviews, improves, and writes SwiftUI code following state management, view composition, performance, and iOS 26+ Liquid Glass best practices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/instruments_parser/xctrace.py
1"""Thin wrapper around the `xctrace` CLI."""2from __future__ import annotations34import subprocess5import xml.etree.ElementTree as ET6from dataclasses import dataclass7from pathlib import Path8910@dataclass(frozen=True)11class RunInfo:12"""Per-run metadata and schemas. Instruments traces can hold multiple runs."""13number: int14template_name: str | None15duration_s: float | None16start_date: str | None17end_date: str | None18schemas: frozenset[str]192021@dataclass(frozen=True)22class TraceInfo:23xctrace_version: str24runs: tuple[RunInfo, ...]2526def get_run(self, number: int) -> RunInfo:27for r in self.runs:28if r.number == number:29return r30available = ", ".join(str(r.number) for r in self.runs)31raise KeyError(f"run {number} not in trace (available: {available})")323334def version() -> str:35out = subprocess.run(36["xctrace", "version"], capture_output=True, text=True, check=True37)38return out.stdout.strip()394041def toc(trace_path: Path) -> TraceInfo:42"""Export the trace's table of contents and return per-run metadata.4344The TOC is small (a few KB) so we load it fully rather than streaming.45"""46xml_bytes = _run_export(trace_path, ["--toc"])47root = ET.fromstring(xml_bytes)4849instruments = _find_text(root, ".//instruments-version") or ""5051runs: list[RunInfo] = []52for run_el in root.iterfind("./run"):53number_attr = run_el.get("number")54if not number_attr:55continue56try:57number = int(number_attr)58except ValueError:59continue60if number <= 0:61continue6263schemas: set[str] = set()64for table in run_el.iterfind("./data/table"):65schema = table.get("schema")66if schema:67schemas.add(schema)6869summary = run_el.find("./info/summary")70if summary is not None:71template = _find_text(summary, "./template-name")72duration = _find_text(summary, "./duration")73start = _find_text(summary, "./start-date")74end = _find_text(summary, "./end-date")75else:76template = duration = start = end = None7778runs.append(RunInfo(79number=number,80template_name=template,81duration_s=float(duration) if duration else None,82start_date=start,83end_date=end,84schemas=frozenset(schemas),85))8687runs.sort(key=lambda r: r.number)88return TraceInfo(89xctrace_version=instruments,90runs=tuple(runs),91)929394def export_schema(trace_path: Path, schema: str, run: int = 1) -> bytes:95"""Export one schema's data as XML bytes from the given run.9697Callers are expected to iterparse the result rather than build a full tree98for large schemas (time-profile can be tens of MB).99"""100xpath = f'/trace-toc/run[@number="{run}"]/data/table[@schema="{schema}"]'101return _run_export(trace_path, ["--xpath", xpath])102103104def _run_export(trace_path: Path, extra_args: list[str]) -> bytes:105cmd = ["xctrace", "export", "--input", str(trace_path), *extra_args]106proc = subprocess.run(cmd, capture_output=True, check=False)107if proc.returncode != 0:108raise RuntimeError(109f"xctrace export failed ({proc.returncode}): "110f"{proc.stderr.decode(errors='replace').strip()}"111)112return proc.stdout113114115def _find_text(root: ET.Element, path: str) -> str | None:116el = root.find(path)117return el.text if el is not None and el.text else None118