Commit Graph

3 Commits

Author SHA1 Message Date
Conor 58e9815fdb feat: --symbolicate via xcrun atos (HangBuster PR 3/3) (#93)
Closes the stack-enrichment loop: with this PR, --get-details --cluster N
--symbolicate rewrites every [0x...] frame in a captured sample/spindump
stack into 'Foo.bar (Foo.swift:42)' notation via atos.

Pipeline (pure):
- extract_stack_addresses(stack) — pulls unique 0x... tokens in order
- symbolicate_stack(stack, resolver) — takes a resolver callable, rewrites
  inline. No I/O. Resolver-injected so it's trivially unit-testable.
  Addresses atos echoes back unchanged are left as-is, never bloated with
  a redundant arrow.

Watcher:
- _run_atos(binary, addrs) batches addresses in one subprocess; failures
  (timeout / non-zero / missing xcrun) return {} so the original stack
  survives untouched.
- _resolve_symbolication_target picks --dsym over --app-binary, falling
  back to IOS_SIM_HANG_DSYM then IOS_SIM_HANG_APP_BINARY env vars.
- _apply_symbolication walks cluster.auto_samples in place; sets
  symbolicated=True on each rewritten sample.

CLI:
- New --symbolicate flag on --get-details
- --app-binary <path> and --dsym <path> with env-var fallbacks
- No worker hot-path impact — symbolication is strictly post-capture

Tests: 11 new units covering address extraction, symbolicate happy path
+ no-op cases, atos subprocess wrapper (success/timeout/non-zero/empty
input), and the target-resolution precedence (flag > env > none).
2026-05-25 09:34:56 +01:00
Conor Luddy b3a8be9e54 feat: --auto-spindump + multi-kind cluster sidecar (HangBuster PR 2/3)
Adds spindump capture alongside --auto-sample, and fixes a latent gap
where auto-samples stashed during capture never made it into summary.json.

Storage: read_auto_samples returns dict[str, list[dict]] (write-order
preserved) so a fingerprint can carry both a simctl-sample and a spindump
record. Worker uses parallel dedup sets so each kind fires at most once
per fingerprint.

Pipeline: Cluster gains auto_samples: list[dict] | None alongside the
legacy auto_sample (kept for backward compat with old summary.json
files). SummaryBuilder.build() now takes auto_samples_by_fp and attaches
matching captures to clusters. SessionStore.build_summary passes it
through automatically. format_cluster_detail iterates and renders each
kind with its own label; also fixes a bug where the old format treated
stack as a list when PR 1 made it text — now splits by lines correctly.

CLI: new --auto-spindump flag, parallel to --auto-sample. Updated
--resample to use the new auto_samples list shape.

Tests: 8 new units covering spindump subprocess paths, multi-kind
storage round-trip, SummaryBuilder attachment by fingerprint, multi-
kind format rendering, and legacy auto_sample backward compat.
2026-05-25 08:46:15 +01:00
Conor Luddy 0f108dc36e feat: implement --auto-sample via xcrun simctl spawn sample
Replaces the soft-imported main_thread_sampler stub (issue #62) with a
direct shell-out to the in-simulator sample binary. On each new hang
fingerprint, the worker captures a 1s main-thread stack and stashes it
on the cluster.

- _attempt_auto_sample now takes (udid, pid) and shells to
  xcrun simctl spawn <udid> sample <pid> 1 -mayDie -file -
- Return shape gains kind/captured_at_ms/symbolicated fields so future
  enrichments (spindump, atos) can share the auto_sample slot
- Threaded udid through _read_stream_into_events and the --resample
  path in get_details
- Help text updated to reflect the simctl backend

Tests: 7 new units mocking subprocess.run for success/timeout/non-zero/
missing-udid/missing-pid/empty-stdout/xcrun-missing paths.
2026-05-24 19:46:58 +01:00