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/time_profiler.py
1"""Time Profiler lane parser (schema `time-profile`).23Aggregates CPU samples by leaf symbol, keeps per-sample rows so that other4lanes can correlate by timestamp window.5"""6from __future__ import annotations78from collections import defaultdict9from pathlib import Path10from typing import Any1112from . import xctrace, xml_utils1314PREFERRED_SCHEMAS = ("time-profile",)15FALLBACK_SCHEMAS = ("time-sample",) # no symbolication; used only if nothing else161718def analyze(19trace_path: Path,20toc_schemas: frozenset[str],21top_n: int = 10,22window: tuple[int, int] | None = None,23run: int = 1,24) -> dict[str, Any]:25schema = _pick_schema(toc_schemas)26if schema is None:27return {28"lane": "time-profiler",29"available": False,30"notes": ["Time Profiler data not present in trace."],31}3233xml_bytes = xctrace.export_schema(trace_path, schema, run=run)34stream = xml_utils.RowStream(xml_bytes)3536samples: list[dict] = []37symbol_weight: dict[str, int] = defaultdict(int)38symbol_samples: dict[str, int] = defaultdict(int)39symbol_thread: dict[str, str] = {}40processes: set[str] = set()41total_weight = 042min_time: int | None = None43max_time: int | None = None4445for row in stream:46time_el = row.get("time")47weight_el = row.get("weight")48thread_el = row.get("thread")49stack_el = row.get("stack")50if stack_el is None or time_el is None or thread_el is None:51continue5253sample_time_ns = xml_utils.int_text(stream.resolve(time_el))54if not xml_utils.in_window(sample_time_ns, window):55continue56weight_ns = xml_utils.int_text(stream.resolve(weight_el)) or 057frames = xml_utils.extract_backtrace(stack_el, stream, max_frames=20)58if not frames:59continue6061thread = xml_utils.extract_thread(thread_el, stream)62process_name = (thread.get("process") or {}).get("name")63if process_name:64processes.add(process_name)6566leaf = xml_utils.top_symbol(frames)67symbol_weight[leaf] += weight_ns68symbol_samples[leaf] += 169symbol_thread.setdefault(70leaf, "main" if thread["is_main"] else thread.get("name", "")71)72total_weight += weight_ns7374if sample_time_ns is not None:75min_time = sample_time_ns if min_time is None else min(min_time, sample_time_ns)76max_time = sample_time_ns if max_time is None else max(max_time, sample_time_ns)7778samples.append({79"time_ns": sample_time_ns,80"weight_ns": weight_ns,81"thread_name": thread["name"],82"is_main": thread["is_main"],83"process": process_name,84"leaf_symbol": leaf,85"frames": frames[:5],86})8788samples.sort(key=lambda s: s["time_ns"])8990top = sorted(91symbol_weight.items(), key=lambda kv: kv[1], reverse=True92)[:top_n]93top_offenders = [94{95"symbol": sym,96"weight_ns": w,97"weight_ms": round(w / 1_000_000, 2),98"samples": symbol_samples[sym],99"percent": round(100.0 * w / total_weight, 2) if total_weight else 0.0,100"thread": symbol_thread.get(sym, ""),101}102for sym, w in top103]104105notes: list[str] = []106if schema in FALLBACK_SCHEMAS:107notes.append(108f"Using fallback schema `{schema}`; backtraces may be unsymbolicated."109)110111return {112"lane": "time-profiler",113"available": True,114"schema_used": schema,115"metrics": {116"total_samples": len(samples),117"total_weight_ns": total_weight,118"total_weight_ms": round(total_weight / 1_000_000, 2),119"window_start_ns": min_time,120"window_end_ns": max_time,121"processes": sorted(processes),122},123"top_offenders": top_offenders,124"notes": notes,125# Internal: retained for correlation. Stripped before JSON emission126# if --slim is requested by the orchestrator.127"_samples": samples,128}129130131def _pick_schema(available: frozenset[str]) -> str | None:132for s in PREFERRED_SCHEMAS + FALLBACK_SCHEMAS:133if s in available:134return s135return None136