feat(crash): Breakpad-style minidumps on macOS and Windows

Ghostty links sentry-native with the breakpad backend on every platform
(SharedDeps.zig), so it writes minidumps on macOS and Windows as well.
- macOS: a Mach exception port handler thread (structured like
  Breakpad's exception_handler) catches EXC_BAD_ACCESS/BAD_INSTRUCTION/
  ARITHMETIC/BREAKPOINT, suspends other threads, reads the crashed
  thread state and writes a minidump via minidump-writer as the
  event.minidump attachment, then restores the previous ports so the
  BSD signal and default termination follow. The signal path remains
  the fallback (and handles SIGABRT); a watchdog alarm bounds dumping.
- Windows: SetUnhandledExceptionFilter hands off to a pre-started dump
  thread using minidump-writer's dump_crash_context.
- The screen-file open action decodes the escaped path to raw OS bytes
  (also for the C action passed to embedders).

macOS/Windows code is compile-checked (clippy -D warnings) only; the
shared Mach message decoding and minidump attachment logic have Linux
unit tests. Platform matrix in docs/rewrite/notes/crash-CR.md.

Test Plan:
- cargo test --workspace (3348 passed)
- just check-macos; just check-macos x86_64-apple-darwin; just check-windows
This commit is contained in:
ddidderr committed 2026-10-12 08:55:27 +02:00
1 parent 162aa260f1
commit 72bfac1cc8
12 files changed
+914 -34

No files matched your search

+11
View File
@@ -79,6 +79,17 @@ pub(crate) unsafe fn capture(
// state; the kernel built it for this signal frame.
let ss = unsafe { uc.uc_mcontext.add(EXCEPTION_STATE_SIZE) };
// SAFETY: `ss` points at the thread state in the kernel built mcontext.
unsafe { fill_thread_state(n, ss) };
}
/// Fill registers and the frame-pointer stack walk from a thread state
/// (`x86_thread_state64_t` / `arm_thread_state64_t`, as found in a
/// `ucontext` or returned by `thread_get_state`).
///
/// # Safety
/// `ss` must point to a valid, aligned thread state of the native flavor.
pub(crate) unsafe fn fill_thread_state(n: &mut Native, ss: *const u8) {
#[cfg(target_arch = "x86_64")]
{
// SAFETY: as above; the layout is `x86_thread_state64_t`.