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
6.1 KiB
6.1 KiB
Z3 final fixes (vt-capi, shadertoy, tripwire, crash, kitty debug dump, bench signpost)
Supersedes the stale statements in libghostty-vt.md ("interior collections use the global allocator"), crash-CR.md ("no minidump") and macos-M1-renderer.md ("iChannelTime stride limitation").
1. C allocator governs parser storage (lib/allocator.zig, terminal/c/osc.zig)
ghostty-vt::oscgets a small allocator abstraction: traitByteStorage(fallibletry_reserve/try_extend,resetreleases growth) behindParser::with_storage; default stays aVec<u8>(also implements the trait). A failed grow sets the parser state invalid, exactly like Zig'sWriteFailed; a failed 2048-byte reserve when switching to the allocating writer falls back to the fixed buffer (ZiginitCapacity catch).ghostty-vt-capi/alloc.rs:CByteStorage(payload buffer, 2048 bytes allocated atghostty_osc_new, growth by alloc-copy-free through the vtable) andCBlock(NUL-terminated title copy).ghostty_osc_newreturnsGHOSTTY_OUT_OF_MEMORYwhen the parser object or its buffer cannot be allocated; if the title copy fails,ghostty_osc_endreports an invalid command.OscCommandnow holds the type key + C-allocated title only.- Remaining global-allocator use: transient
Commandpayload copies made insideghostty_osc_end(Zig borrows the parser buffer); they never outlive the call. - Tests:
tests/c/alloc_test.c+tests/c_allocator.rs(counting/failing/limiting C allocator: leak-free accounting, OOM at each creation allocation, live-bytes limit invalidating a large OSC 52, title-copy failure, mid-payload failure then reuse); unit teststorage_failure_invalidates_oscin ghostty-vt.
2. Shadertoy
shadertoy_sampler.rs: functions keyed by full signature (per-parameter sampler layout, all overloads kept); a call picks the overload by arity and which arguments are samplers (iChannelNor a sampler param of the enclosing function). Testsoverloads_are_keyed_by_signature,overloads_through_sampler_parameters.shadertoy.rs:msl_from_compiledpost-processes naga's MSL: uniform-block scalar arrays with std140 stride (iChannelTime[4], found by reflection over the naga module, not by name) becomemetal::vec<T,N> inner[K]wrappers and element reads get.x. Limitation: copying the whole array into a local variable is not rewritten. Testmsl_uniform_block_matches_std140_upload_layoutparses the emittedstruct Globals, computes MSL offsets/size and compares every member tooffset_of!(Uniforms, ..)andsize_of::<Uniforms>()(4496).
3. Tripwire (tripwire.zig)
ghostty-terminal/src/tripwire.rs:tripwire_module!(name, PointEnum, Error)->name::{check, error_always, error_after, end(ResetMode), reset};endreturnsUntrippedError(warn-logs untripped points); state is thread-local so parallel tests are isolated;checkis a no-op outsidecfg(test). 5 tests ported/added (the 3 Zig tests + retain/reset + thread isolation).- Ported OOM tests:
Tabstops: resize alloc failure preserves state(tabstops.rs,resize_tw);PageList init error(page_list.rs,init_tw+init_pages_tw; Ziginit_memory_poolhas no Rust analogue (no pool),viewport_pin/viewport_pin_trackare both checked before the singletrack_pinreserve); bothreloadActive partial history cleanuptests (search/screen.rs,reload_active_tw; assert OOM is returned andhistory_resultsis unchanged). - Not portable (infallible Rust code, documented):
selectionString map allocation failure cleanup(selection_string*returnString/Vec, no fallible copy; nothing to unwind). Font: Atlasinit/reserve/growerrors (Atlas storage is infallibleVec) and SharedGridinit error(no allocation points; the render-rollback test already exists asrender_glyph_error_after_cache_insert_rolls_back_cache_entry). ghostty-font can useghostty_terminal::tripwireif fallible paths are added later. - Fixed the
enum_variant_namesclippy lint reported onInitPagesPoint(variants renamedNode/BufStd/BufNonStd).
4. Crash minidump (crash/sentry.zig)
- Linux fatal-signal path: handler keeps building head+event in static memory, then captures
siginfo+ machine context (+FP state) into a staticcrash_context::CrashContextand callsminidump::capture: rawclone(fork semantics, own 4 MiB mmap'd stack because the handler runs on a tiny altstack, no atfork handlers),PR_SET_PTRACER_ANY, parent pollswaitpidwith a 20 s timeout. The child usesminidump-writer 0.13(MinidumpWriterConfig+set_crash_context, the crate's documented clone-and-ptrace pattern for the local process) and writes the full envelope: event +minidump.dmpattachment (attachment_type: event.minidump). Any failure falls back to the previous event-only envelope. Stack overflow: if the SP page is unreadable (guard page) the writer is retried with SP moved to the first readable page above it (reported RSP is then approximate, stack contents captured). - Tests (
crates/ghostty-crash/tests/minidump.rs): SIGSEGV, SIGABRT and stack overflow on a spawned thread; parse with theminidumpcrate; assert thread list contains the crashing tid with stack memory and the main thread, exception stream blames that tid with the signal number and registers, modules/memory lists present. Existing roottests/crash.rsstill pass. - Not done: macOS/Windows native minidumps (sentry-native uses its own backend there); no symbol upload.
5. kitty debugDump, bench signpost
LoadingImage::{debug_dump_filename, debug_dump_to, debug_dump}(debug builds only), same file name format as Zig; test.ghostty-bench/src/signpost.rs(apple only):os_log_create+os_signpost_id_make_with_pointer+_os_signpost_emit_with_name_implinterval begin/end around the benchmark loop, subsystemcom.mitchellh.ghostty, category PointsOfInterest, nameghostty. Compile/clippy-checked for aarch64-apple-darwin only (not run).
Gate
workspace cargo clippy --all-targets -D warnings, cargo +nightly fmt --check, cargo test --workspace pass;
aarch64-apple-darwin clippy passes for bench, renderer, crash, terminal, vt-capi.