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:
1 parent
96314c1a8b
commit
f6002d7519
22 files changed
+2079
-57
No files matched your search
@@ -196,6 +196,29 @@ pub struct PageList {
|
||||
pin_serial: u64,
|
||||
pause_checks: usize,
|
||||
}
|
||||
/// Failure points of [`PageList::init`] (Zig: `init_tw`). The Zig
|
||||
/// `init_memory_pool` point has no counterpart: Rust has no up-front memory
|
||||
/// pool, the slabs start empty and grow with the fallible reserves below.
|
||||
/// `viewport_pin` and `viewport_pin_track` are two allocations in Zig (pool
|
||||
/// create + set insert) that are one reserve in `track_pin` here; both points
|
||||
/// are checked before it.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum InitPoint {
|
||||
InitPages,
|
||||
ViewportPin,
|
||||
ViewportPinTrack,
|
||||
}
|
||||
crate::tripwire_module!(init_tw, InitPoint, Error);
|
||||
|
||||
/// Failure points of the page creation done by `init` (Zig: `initPages_tw`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum InitPagesPoint {
|
||||
Node,
|
||||
BufStd,
|
||||
BufNonStd,
|
||||
}
|
||||
crate::tripwire_module!(init_pages_tw, InitPagesPoint, Error);
|
||||
|
||||
fn std_size() -> usize {
|
||||
Page::layout(page::STD_CAPACITY).total_size
|
||||
}
|
||||
@@ -242,12 +265,15 @@ impl PageList {
|
||||
pin_serial: 0,
|
||||
pause_checks: 0,
|
||||
};
|
||||
init_tw::check(InitPoint::InitPages)?;
|
||||
while s.total_rows < usize::from(rows) {
|
||||
s.grow()?;
|
||||
}
|
||||
let pin = s
|
||||
.get_top_left(Tag::Screen)
|
||||
.ok_or(Error::InvalidCoordinate)?;
|
||||
init_tw::check(InitPoint::ViewportPin)?;
|
||||
init_tw::check(InitPoint::ViewportPinTrack)?;
|
||||
s.viewport_pin = Some(s.track_pin(pin)?);
|
||||
Ok(s)
|
||||
}
|
||||
@@ -269,6 +295,7 @@ impl PageList {
|
||||
if Page::layout(cap).total_size > u32::MAX as usize {
|
||||
return Err(Error::OutOfSpace);
|
||||
}
|
||||
init_pages_tw::check(InitPagesPoint::Node)?;
|
||||
self.nodes.try_reserve(1).map_err(|_| Error::OutOfMemory)?;
|
||||
let mut data = if let Some(mut p) = self.clean_pool.pop().filter(|p| p.capacity == cap) {
|
||||
p.size = page::Size {
|
||||
@@ -281,6 +308,11 @@ impl PageList {
|
||||
p.reinit()?;
|
||||
p
|
||||
} else {
|
||||
if Page::layout(cap).total_size <= std_size() {
|
||||
init_pages_tw::check(InitPagesPoint::BufStd)?;
|
||||
} else {
|
||||
init_pages_tw::check(InitPagesPoint::BufNonStd)?;
|
||||
}
|
||||
Page::init(cap)?
|
||||
};
|
||||
data.size.rows = 0;
|
||||
@@ -2972,6 +3004,55 @@ mod tests {
|
||||
s.clear_dirty();
|
||||
}
|
||||
#[test]
|
||||
fn page_list_init_error() {
|
||||
use crate::tripwire::ResetMode;
|
||||
// Test every failure point in `init` and ensure we exit early
|
||||
// without panicking or leaving the tripwire armed.
|
||||
for tag in [
|
||||
InitPoint::InitPages,
|
||||
InitPoint::ViewportPin,
|
||||
InitPoint::ViewportPinTrack,
|
||||
] {
|
||||
init_tw::error_always(tag, Error::OutOfMemory);
|
||||
assert_eq!(
|
||||
Err(Error::OutOfMemory),
|
||||
PageList::init(80, 24, None).map(drop)
|
||||
);
|
||||
init_tw::end(ResetMode::Reset).unwrap();
|
||||
}
|
||||
|
||||
// init creates pages transitively, so check each failure there too.
|
||||
let big = page::STD_CAPACITY.max_cols().unwrap() + 1;
|
||||
for tag in [
|
||||
InitPagesPoint::Node,
|
||||
InitPagesPoint::BufStd,
|
||||
InitPagesPoint::BufNonStd,
|
||||
] {
|
||||
init_pages_tw::error_always(tag, Error::OutOfMemory);
|
||||
let cols = if tag == InitPagesPoint::BufStd {
|
||||
80
|
||||
} else {
|
||||
big
|
||||
};
|
||||
assert_eq!(
|
||||
Err(Error::OutOfMemory),
|
||||
PageList::init(cols, 24, None).map(drop)
|
||||
);
|
||||
init_pages_tw::end(ResetMode::Reset).unwrap();
|
||||
}
|
||||
|
||||
// Non-standard pages: fail on the second one.
|
||||
init_pages_tw::error_after(InitPagesPoint::BufNonStd, Error::OutOfMemory, 1);
|
||||
assert_eq!(
|
||||
Err(Error::OutOfMemory),
|
||||
PageList::init(big, page::STD_CAPACITY.rows + 1, None).map(drop)
|
||||
);
|
||||
init_pages_tw::end(ResetMode::Reset).unwrap();
|
||||
|
||||
// And with nothing armed init still works.
|
||||
PageList::init(80, 24, None).unwrap();
|
||||
}
|
||||
#[test]
|
||||
fn page_list_init_rows_across_two_pages() {
|
||||
let mut cols = 50;
|
||||
while initial_capacity(cols).rows >= 100 {
|
||||
|
||||
Reference in new issue
Block a user