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
73 lines
6.1 KiB
Markdown
73 lines
6.1 KiB
Markdown
# 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::osc` gets a small allocator abstraction: trait `ByteStorage` (fallible `try_reserve`/`try_extend`,
|
|
`reset` releases growth) behind `Parser::with_storage`; default stays a `Vec<u8>` (also implements the trait).
|
|
A failed grow sets the parser state invalid, exactly like Zig's `WriteFailed`; a failed 2048-byte reserve when
|
|
switching to the allocating writer falls back to the fixed buffer (Zig `initCapacity catch`).
|
|
* `ghostty-vt-capi/alloc.rs`: `CByteStorage` (payload buffer, 2048 bytes allocated at `ghostty_osc_new`, growth by
|
|
alloc-copy-free through the vtable) and `CBlock` (NUL-terminated title copy). `ghostty_osc_new` returns
|
|
`GHOSTTY_OUT_OF_MEMORY` when the parser object or its buffer cannot be allocated; if the title copy fails,
|
|
`ghostty_osc_end` reports an invalid command. `OscCommand` now holds the type key + C-allocated title only.
|
|
* Remaining global-allocator use: transient `Command` payload copies made inside `ghostty_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 test `storage_failure_invalidates_osc` in 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 (`iChannelN` or a sampler param of the enclosing
|
|
function). Tests `overloads_are_keyed_by_signature`, `overloads_through_sampler_parameters`.
|
|
* `shadertoy.rs`: `msl_from_compiled` post-processes naga's MSL: uniform-block scalar arrays with std140 stride
|
|
(`iChannelTime[4]`, found by reflection over the naga module, not by name) become `metal::vec<T,N> inner[K]` wrappers
|
|
and element reads get `.x`. Limitation: copying the whole array into a local variable is not rewritten.
|
|
Test `msl_uniform_block_matches_std140_upload_layout` parses the emitted `struct Globals`, computes MSL
|
|
offsets/size and compares every member to `offset_of!(Uniforms, ..)` and `size_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}`; `end` returns `UntrippedError` (warn-logs untripped points); state is
|
|
thread-local so parallel tests are isolated; `check` is a no-op outside `cfg(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`; Zig `init_memory_pool` has no Rust analogue
|
|
(no pool), `viewport_pin`/`viewport_pin_track` are both checked before the single `track_pin` reserve);
|
|
both `reloadActive partial history cleanup` tests (search/screen.rs, `reload_active_tw`; assert OOM is returned
|
|
and `history_results` is unchanged).
|
|
* Not portable (infallible Rust code, documented): `selectionString map allocation failure cleanup`
|
|
(`selection_string*` return `String`/`Vec`, no fallible copy; nothing to unwind). Font: Atlas `init/reserve/grow`
|
|
errors (Atlas storage is infallible `Vec`) and SharedGrid `init error` (no allocation points; the
|
|
render-rollback test already exists as `render_glyph_error_after_cache_insert_rolls_back_cache_entry`).
|
|
ghostty-font can use `ghostty_terminal::tripwire` if fallible paths are added later.
|
|
* Fixed the `enum_variant_names` clippy lint reported on `InitPagesPoint` (variants renamed `Node/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 static `crash_context::CrashContext` and calls `minidump::capture`: raw
|
|
`clone` (fork semantics, own 4 MiB mmap'd stack because the handler runs on a tiny altstack, no atfork handlers),
|
|
`PR_SET_PTRACER_ANY`, parent polls `waitpid` with a 20 s timeout. The child uses `minidump-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.dmp` attachment (`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 the `minidump` crate; 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 root `tests/crash.rs` still 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_impl` interval begin/end around the benchmark loop, subsystem `com.mitchellh.ghostty`,
|
|
category PointsOfInterest, name `ghostty`. 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.
|