feat(crash): port Ghostty's local crash reporting

Ports ZIG/src/crash/* as crates/ghostty-crash. Like Ghostty (which uses
sentry-native only to write local files), crashes are written as Sentry
envelopes (<uuid>.ghosttycrash) to $XDG_STATE_HOME/ghostty/crash:
- envelope parse/serialize (all Zig tests ported)
- panic hook plus SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT handlers on an
  alternate stack; the signal path is allocation-free and re-raises with
  the default action so exit status and core dumps are unchanged
- thread-local crash context (main/renderer/io/read thread, surface
  dimensions) set from the corresponding threads
- `sentry` cargo feature on the root binary, off by default like
  upstream's -Dsentry; +crash-report lists exactly what is written

Deviation: attachments always carry "type":"attachment" (Zig's
in-memory attachments omit it and become unparseable).

Test Plan:
- cargo test -p ghostty-crash (19)
- cargo test --test crash (child panics, SIGSEGV, stack overflow and
  abort each produce a valid envelope listed by +crash-report)
This commit is contained in:
ddidderr committed 2026-10-12 06:02:28 +02:00
1 parent a07b224869
commit a714fd9a65
11 files changed
+1933 -36

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
//! Local crash handling (port of `ghostty/src/crash`).
//!
//! This covers setting up the process to catch crashes, writing crash
//! reports (Sentry envelopes) to disk, and introspecting them. Nothing is
//! ever sent over the network.
// Fatal-signal handling does not exist on Windows (Zig skips crash capture
// there), so parts of the shared report writer are unused.
#![cfg_attr(windows, allow(dead_code))]
pub mod context;
pub mod dir;
pub mod envelope;
pub mod sentry;
#[cfg(unix)]
mod signal;
#[cfg(windows)]
#[path = "signal_windows.rs"]
mod signal;
pub use context::{DimsSnapshot, SurfaceDims, ThreadGuard, ThreadType, current_dims};
pub use dir::{Dir, Report, ReportIterator, default_dir};
pub use envelope::Envelope;
pub use sentry::{Options, deinit, init, install};