import { z } from "zod"; import type { AnalyzeTraceOptions, DataStatus, SupportStatus } from "../types.js"; export declare const analyzeHangsSchema: z.ZodObject<{ tracePath: z.ZodString; topN: z.ZodDefault; minDurationMs: z.ZodDefault; timeRangeMs: z.ZodOptional>; topFramesByHangStartNs: z.ZodOptional>; includeStackClassification: z.ZodDefault; outputFormat: z.ZodOptional>; }, "strip", z.ZodTypeAny, { tracePath: string; topN: number; minDurationMs: number; includeStackClassification: boolean; outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined; timeRangeMs?: { startMs: number; endMs: number; } | undefined; topFramesByHangStartNs?: Record | undefined; }, { tracePath: string; outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined; topN?: number | undefined; minDurationMs?: number | undefined; timeRangeMs?: { startMs: number; endMs: number; } | undefined; topFramesByHangStartNs?: Record | undefined; includeStackClassification?: boolean | undefined; }>; export type AnalyzeHangsInput = z.infer; /** * Catalog of main-thread-violation signatures. Each entry classifies a * top-frame symbol pattern into one of four kinds that map onto the most * common iOS user-perceived freezes: * * - `sync-io`: a blocking POSIX read/write or Foundation file API the * runtime cannot async away from the main queue. * - `db-lock`: SQLite mutex acquisition (the underlying primitive for * Core Data, GRDB, and most Swift ORMs). * - `network`: a blocking Network.framework/NSURLConnection sync call. * - `lock-contention`: pthread/os_unfair_lock acquisition on the main * thread, which serializes us against another thread. * * The matchers are case-sensitive substring checks. They deliberately * stay close to the symbol name DebugSwift's Thread Checker flags so the * coverage gap between the on-device tool and the offline catalog stays * small. Adding new symbols later is a one-line append. */ export type MainThreadViolationKind = "sync-io" | "db-lock" | "network" | "lock-contention"; export interface MainThreadViolation { kind: MainThreadViolationKind; topFrame: string; samples: number; } /** * Pure: classify a top-frame symbol into a `MainThreadViolation`. Returns * `null` when nothing in the catalog matches. The `samples` count comes * from the caller; with only a top-frame string available we set it to 1. * * Multiple signatures can match a single frame (e.g. a sync I/O call that * also holds an unfair lock). We return the FIRST match in catalog order, * which puts more user-actionable categories ahead of generic locks. */ export declare function classifyHangFrame(topFrame: string, samples?: number): MainThreadViolation | null; /** Stable key used to correlate the supplemental `topFramesByHangStartNs` * map. Hang startNs values are nanoseconds (integers when xctrace exports * them cleanly), so the key is just `String(startNs)`. Centralized so * callers building the map use the same convention. */ export declare function hangFrameMapKey(startNs: number): string; export interface HangEntry { startNs: number; startFmt: string; durationNs: number; durationMs: number; durationFmt: string; hangType: string; /** Main-thread violations detected from the supplemental top-frame map. * Empty array when the caller provided a frame but no signature matched; * undefined when no frame was provided for this hang at all. */ mainThreadViolations?: MainThreadViolation[]; } /** * Entry from the `hang-risks` schema. v1.14. * * Different shape from `HangEntry`: `hang-risks` reports point-in-time RISK * annotations emitted by the iOS runtime (e.g. "Hang Risk", "Severe Hang * Risk" narrative events) rather than measured durations. No `durationNs` * field exists because risks have no duration. The Severity column buckets * the annotation; `backtrace` is a stringified stack at the moment the * risk was annotated. */ export interface HangRiskEntry { timestampNs: number; timestampFmt: string; severity: string; eventType: string; message: string; threadName?: string; backtrace?: string; } export interface AnalyzeHangsResult { ok: boolean; tracePath: string; totals: { rows: number; hangs: number; microhangs: number; longestMs: number; averageMs: number; totalDurationMs: number; }; /** Filtered + sorted hangs, capped to topN. */ top: HangEntry[]; /** * v1.14: hang-risks schema events. Apple-runtime risk annotations * complementary to the measured potential-hangs above. Absent when * the schema was not present in the trace OR when xctrace failed to * export it; present (possibly empty array) when the schema was * exported successfully. */ risks?: HangRiskEntry[]; /** v1.14: hang-risks aggregates. Mirrors `totals` for `top[]`. Absent when the schema was not exported. */ risksTotals?: { rows: number; bySeverity: Record; }; diagnosis: string; /** * Disambiguates empty arrays into "no data in the trace" vs "trace could * not be exported" vs "data was exported partially". See {@link DataStatus}. * * @deprecated v1.14 item I. Use `supportStatus[]` instead. Kept for * backwards compatibility with v1.13 callers. */ status: DataStatus; /** * v1.14+. Unified per-area status surface. For analyzeHangs this * contains one entry for the `potential-hangs` schema and a second * for `hang-risks` when that schema was discovered. See {@link * SupportStatus}. */ supportStatus: SupportStatus[]; } /** Pure: turn parsed XML rows into our analyzed result. The optional * `hangRisksXml` (v1.14) is parsed via {@link analyzeHangRisksFromXml} * and surfaced on `result.risks[]` + `result.risksTotals`. */ export declare function analyzeHangsFromXml(xml: string, tracePath: string, topN?: number, minDurationMs?: number, timeRangeMs?: { startMs: number; endMs: number; }, topFramesByHangStartNs?: Readonly>, hangRisksXml?: string): AnalyzeHangsResult; /** * Pure: parse `hang-risks` schema XML into structured risk entries. * * v1.14. The hang-risks schema is complementary to potential-hangs: it * carries runtime-emitted "Hang Risk" / "Severe Hang Risk" annotations * with a backtrace at the moment of risk detection but NO measured * duration. Output is sorted by timestamp ascending so callers can see * the chronological order of risks during the recording. * * Returns `{ rows: [], bySeverity: {} }` when the schema is absent. */ export declare function analyzeHangRisksFromXml(xml: string, topN?: number): { rows: HangRiskEntry[]; total: number; bySeverity: Record; }; /** * Pure: walk parsed time-profile rows + hang entries, correlate samples * to hang windows by timestamp, return a `startNs -> topFrame` map. * * Algorithm: for each hang H with [startNs, startNs+durationNs], find all * samples whose `weight` timestamp falls in that window. Per hang, pick * the top frame by aggregate sample weight (or by sample count if weight * is absent). The result map keys are stringified `startNs` values to * match the existing `topFramesByHangStartNs` shape that v1.9 exposed. * * Returns an empty map when the time-profile rows are absent or none * correlate. Failure modes degrade silently so the cycle-side path * still completes. * * Exposed for testing. */ export declare function correlateTimeProfileToHangs(hangs: Array<{ startNs: number; durationNs: number; }>, timeProfileRows: Array<{ startNs: number; weight?: number; backtrace?: string; topFrame?: string; }>): Record; export declare function analyzeHangs(input: AnalyzeHangsInput, options?: AnalyzeTraceOptions): Promise;