fix: C allocator contract, shadertoy overloads/MSL stride, minidumps

Closes the remaining library rows of the final completeness audit
(docs/rewrite/notes/final-Z3.md):
- libghostty-vt: OSC parser storage goes through the caller-supplied C
  allocator via a ByteStorage abstraction; ghostty_osc_new reports
  GHOSTTY_OUT_OF_MEMORY and failed grows invalidate the OSC like Zig.
  Tested with a counting/failing/limiting C allocator.
- shadertoy: sampler parameter rewriting is keyed by full signature so
  overloads work; the Metal uniform block keeps std140 strides for
  iChannelTime[4] (MSL offsets checked against the upload layout).
- tripwire.zig ported (named trip points, hit counts, verification) and
  used to port the previously skipped allocation-failure tests that have
  a fallible Rust path; the rest are documented.
- crash: fatal signals capture siginfo + registers and a forked helper
  writes a minidump (minidump-writer) as an envelope attachment, like
  sentry-native/Breakpad; tests parse it with the minidump crate.
- kitty LoadingImage::debug_dump (debug builds) and the Darwin
  Instruments signpost around benchmark runs.

Test Plan:
- cargo test -p ghostty-vt-capi --test c_allocator
- cargo test -p ghostty-crash --test minidump
This commit is contained in:
ddidderr committed 2026-10-12 08:14:02 +02:00
1 parent 5568f99bae
commit 8af6ab68d3
22 files changed
+2079 -57

No files matched your search

+27 -3
View File
@@ -133,7 +133,7 @@ fn mkdir_p(path: &mut [u8], len: usize) {
/// Write a crash report for `sig`. Returns without doing anything if the
/// crash handler isn't active or another crash is being written.
fn write_signal_report(name: &str, desc: &str) {
fn write_signal_report(name: &str, desc: &str, info: *mut libc::siginfo_t, uc: *mut libc::c_void) {
let Some(st) = sentry::state() else { return };
if BUSY.swap(true, Ordering::Acquire) {
return;
@@ -191,6 +191,30 @@ fn write_signal_report(name: &str, desc: &str) {
// The directory must exist for writing to work.
mkdir_p(&mut sc.path, st.dir.len());
// Preferred: an envelope with a minidump of the whole process, written by
// a forked helper that ptraces us (Linux only; see `minidump`).
#[cfg(target_os = "linux")]
{
// SAFETY: `info`/`uc` are the pointers the kernel gave the handler.
let cc = unsafe { crate::minidump::crash_context(info, uc) };
let cap = crate::minidump::Capture {
path: &sc.path[..=o],
head: &sc.head[..head_len],
event: &sc.event[..ev_len],
};
if crate::minidump::capture(&cap, cc) {
write_all(
2,
b"warning(sentry): crash report with minidump written to disk path=",
);
write_all(2, &sc.path[..o]);
write_all(2, b"\n");
return;
}
}
#[cfg(not(target_os = "linux"))]
let _ = (info, uc);
// SAFETY: path is NUL terminated.
let fd = unsafe {
libc::open(
@@ -212,9 +236,9 @@ fn write_signal_report(name: &str, desc: &str) {
}
}
extern "C" fn handler(sig: libc::c_int, _info: *mut libc::siginfo_t, _ctx: *mut libc::c_void) {
extern "C" fn handler(sig: libc::c_int, info: *mut libc::siginfo_t, ctx: *mut libc::c_void) {
if let Some((_, name, desc)) = SIGNALS.iter().find(|(s, ..)| *s == sig) {
write_signal_report(name, desc);
write_signal_report(name, desc, info, ctx);
}
// Restore the default action and re-raise so the process dies with the
// original signal (core dump, exit status) as if we were never here.