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/correlate.py
1"""Cross-lane correlation: for each hang and top-N worst hitches, aggregate2Time Profiler samples and SwiftUI updates whose timestamps fall inside the3event window [start, start+duration]. Uses bisect so lookups stay O(log N)4per event.5"""6from __future__ import annotations78from bisect import bisect_left, bisect_right9from collections import defaultdict10from typing import Any111213def build(lanes: dict[str, dict], top_hitches: int = 5, top_symbols: int = 5) -> list[dict]:14"""Produce a list of correlation entries.1516`lanes` is a dict keyed by lane name (time-profiler, hangs, hitches,17swiftui) of their analyzer outputs.18"""19tp = lanes.get("time-profiler")20hangs = lanes.get("hangs")21hitches = lanes.get("hitches")22swiftui = lanes.get("swiftui")2324tp_index = _build_time_profile_index(tp)25sui_events = (swiftui or {}).get("_events") if swiftui and swiftui.get("available") else None2627correlations: list[dict] = []2829if hangs and hangs.get("available"):30for h in hangs.get("_events", []):31correlations.append(32_correlate_event(33trigger_lane="hangs",34start_ns=h["start_ns"],35end_ns=h["end_ns"],36extra={"hang_type": h["hang_type"]},37tp_index=tp_index,38sui_events=sui_events,39top_symbols=top_symbols,40)41)4243if hitches and hitches.get("available"):44worst_hitches = hitches.get("_events", [])[:top_hitches]45for hi in worst_hitches:46correlations.append(47_correlate_event(48trigger_lane="hitches",49start_ns=hi["start_ns"],50end_ns=hi["end_ns"],51extra={52"frame_duration_ms": hi["frame_duration_ms"],53"hitch_duration_ms": hi["hitch_duration_ms"],54},55tp_index=tp_index,56sui_events=sui_events,57top_symbols=top_symbols,58)59)6061return correlations626364# --- Internal -------------------------------------------------------------6566def _build_time_profile_index(tp: dict | None):67if not tp or not tp.get("available"):68return None69samples = tp.get("_samples") or []70if not samples:71return None72# Samples are already sorted by time in time_profiler.analyze.73times = [s["time_ns"] for s in samples]74return {"times": times, "samples": samples}757677def _correlate_event(78trigger_lane: str,79start_ns: int,80end_ns: int,81extra: dict,82tp_index: dict | None,83sui_events: list[dict] | None,84top_symbols: int,85) -> dict[str, Any]:86entry: dict[str, Any] = {87"trigger": {88"lane": trigger_lane,89"start_ms": round(start_ns / 1_000_000, 2),90"end_ms": round(end_ns / 1_000_000, 2),91"duration_ms": round((end_ns - start_ns) / 1_000_000, 2),92**extra,93},94}9596if tp_index is not None:97tp = _time_profile_hot_symbols(98tp_index, start_ns, end_ns, top_symbols99)100duration_ns = end_ns - start_ns101# Sample rate is 1ms/sample on standard Time Profiler. If the window102# is N ms long we'd expect ~N main-thread samples if main was fully103# running; fewer means main was blocked (I/O, lock, etc.).104expected_if_running = max(1, duration_ns // 1_000_000)105coverage_pct = min(100.0, 100.0 * tp["samples_main"] / expected_if_running)106entry["time_profiler_main_thread"] = {107"samples_in_window": tp["samples_total"],108"samples_on_main": tp["samples_main"],109"main_running_coverage_pct": round(coverage_pct, 1),110"hot_symbols": tp["hot_symbols"],111}112113if sui_events is not None:114sui_overlap = _swiftui_overlaps(sui_events, start_ns, end_ns)115entry["swiftui_overlapping_updates"] = sui_overlap116117return entry118119120def _time_profile_hot_symbols(121tp_index: dict, start_ns: int, end_ns: int, top_n: int122) -> dict:123"""Return main-thread hot symbols in the given window.124125Hang/hitch/SwiftUI correlations are all main-thread responsiveness126problems, so worker-thread symbols are noise. We also return a coverage127metric — when main was blocked on I/O or a lock, the window will have128far fewer samples than its duration would predict, and that signal is129what tells the agent "this was blocked, not CPU-bound".130"""131times = tp_index["times"]132samples = tp_index["samples"]133lo = bisect_left(times, start_ns)134hi = bisect_right(times, end_ns)135window = samples[lo:hi]136if not window:137return {"samples_total": 0, "samples_main": 0, "hot_symbols": []}138139main_samples = [s for s in window if s["is_main"]]140weight_by_symbol: dict[str, int] = defaultdict(int)141count_by_symbol: dict[str, int] = defaultdict(int)142for s in main_samples:143weight_by_symbol[s["leaf_symbol"]] += s["weight_ns"]144count_by_symbol[s["leaf_symbol"]] += 1145total_weight = sum(weight_by_symbol.values()) or 1146147ranked = sorted(weight_by_symbol.items(), key=lambda kv: kv[1], reverse=True)148hot = []149for symbol, weight in ranked[:top_n]:150hot.append({151"symbol": symbol,152"samples": count_by_symbol[symbol],153"weight_ms": round(weight / 1_000_000, 2),154"percent_of_main": round(100.0 * weight / total_weight, 2),155})156return {157"samples_total": len(window),158"samples_main": len(main_samples),159"hot_symbols": hot,160}161162163def _swiftui_overlaps(164events: list[dict], start_ns: int, end_ns: int165) -> list[dict]:166# Events aren't guaranteed sorted by start_ns here (we sort by duration in167# swiftui.analyze). Linear scan; SwiftUI event counts are typically small.168out: list[dict] = []169for e in events:170if e["end_ns"] < start_ns or e["start_ns"] > end_ns:171continue172out.append({173"view": e["view"],174"duration_ms": e["duration_ms"],175"start_ms": e["start_ms"],176})177# Worst first.178out.sort(key=lambda x: x["duration_ms"], reverse=True)179return out[:10]180