deno
1ec783e6 - perf: remove Linux unwind tables from release artifacts (#36265)

Commit
20 days ago
perf: remove Linux unwind tables from release artifacts (#36265) ## Summary - rebuild `std` and the Linux release crates with frame pointers, while disabling newly generated unwind tables - use the additive frame-pointer panic-trace feature from `denoland/panic` for Linux x86_64 and aarch64 release builds - remove `.eh_frame` and `.eh_frame_hdr` from `deno`, `denort`, and `libdenort.so`, and fail packaging if either a section or `PT_GNU_EH_FRAME` remains - preserve the existing `denort --strip-debug` behavior required by libsui relocation rewriting No rusty_v8 change is required: its Linux build already preserves frame pointers, and an end-to-end panic trace crossed Rust std, Deno, V8 C++, and back into Rust/Tokio after the unwind sections were removed. ## Size impact The `deno` row is a paired x86_64 Linux build from the same source revision. The `denort` and `libdenort.so` rows are exact section-removal projections from the official v2.9.1 artifacts; their eventual CI sizes may differ slightly because this change also affects code generation by forcing frame pointers. | artifact | measurement | before | after | reduction | | --- | --- | ---: | ---: | ---: | | `deno` | raw | 114,490,288 | 104,278,808 | 10,211,480 bytes (9.74 MiB, 8.92%) | | `deno` | zip | 45,459,265 | 42,248,545 | 3,210,720 bytes (3.06 MiB) | | `denort` | raw projection | 108,341,376 | 101,910,464 | 6,430,912 bytes (6.13 MiB) | | `denort` | zip projection | 36,527,614 | 34,509,638 | 2,017,976 bytes (1.92 MiB) | | `libdenort.so` | raw projection | 85,556,984 | 79,089,248 | 6,467,736 bytes (6.17 MiB) | | `libdenort.so` | zip projection | 33,358,112 | 31,323,429 | 2,034,683 bytes (1.94 MiB) | In the paired `deno` baseline, `.eh_frame_hdr` was 1,342,324 bytes and `.eh_frame` was 8,659,384 bytes. Rebuilding Rust std and dependencies with unwind tables disabled reduced `.eh_frame` to 3,184,596 bytes; final section removal accounts for the remainder. The small `.gcc_except_table` section is retained. ## Tradeoffs - `-Zbuild-std` is an unstable Cargo interface. This uses `RUSTC_BOOTSTRAP=1` only in the two Linux release jobs, retaining Deno's pinned stable toolchain instead of adding a separately pinned nightly release toolchain. It is still an unsupported escape hatch whose behavior can change with Rust/Cargo updates. - Rebuilding `core`, `alloc`, `std`, `proc_macro`, and `panic_abort` adds release build time, cache entries, and disk pressure. The separate `denort_desktop` invocation uses the same build-std configuration and should reuse those artifacts. - Frame pointers can modestly affect code size and generated-code performance, particularly on x86_64 where a general-purpose register becomes unavailable. A paired 150-run startup smoke test showed no regression (30.097 ms baseline median versus 28.791 ms new median), but this is not a comprehensive performance result. - Stack walking now depends on an intact frame-pointer chain. Rust std is rebuilt, Rust crates are forced to retain frame pointers, and V8 already retains them, but another native library built without frame pointers can truncate a trace. The walker stops safely when it cannot read or validate the next frame. - Removing DWARF CFI means profilers, crash reporters, and other external tools that require CFI cannot unwind the packaged images; frame-pointer-aware tools can. This also assumes no linked native component needs CFI for runtime language-exception unwinding. Rust uses `panic=abort` here and V8 is built without C++ exceptions, but this remains a compatibility boundary for future native dependencies, especially in `libdenort.so`. - Symcache generation still happens before stripping. Deno's built-in panic report uses the frame-pointer walker; `denort` and `libdenort.so` do not gain the same built-in panic URL, though external frame-pointer walking remains possible. - `denort` and `libdenort.so` lose their remaining backtrace path, not just the panic URL. `deno_panic` is wired into the `deno` crate only (`cli/lib.rs`); `denort` installs no panic hook and `libdenort.so`'s hook reports the panic message and `panic_info.location()` without walking the stack. Previously `strip --strip-debug ./denort` kept both `.eh_frame` and `.symtab`, so `RUST_BACKTRACE=1` on a `deno compile` binary produced a symbolized backtrace; with the unwind sections removed libunwind has nothing to walk, and the default Rust hook's `note: run with RUST_BACKTRACE=1 …` line is now a dead end on Linux. This is accepted deliberately: we have no panic reports originating from compiled binaries, so the ~6 MiB shrink on every one of them is worth more than a backtrace path nobody is using. Restoring it later means adding a frame-pointer panic hook to `denort` plus per-binary symcaches — `tools/release/create_symcache.ts` only symbolicates `Deno.execPath()`, and panic.deno.com resolves by version and target alone (`https://dl.deno.land/${type}/${version}/deno-${target}.symcache`), so a `denort` trace URL would currently be symbolicated against `deno`'s symcache. - The `deno_panic` dependency is the published crates.io `0.2.1` release; the sources are identical to the `frame-pointer` branch commit `4ce17fe3cc2dc9557e53013620132e997fdda06b` this PR originally pinned. - Scope is limited to Linux x86_64 and aarch64 release artifacts. Other build profiles and operating systems retain the existing libunwind path and unwind metadata. ## Validation - `./tools/format.js` - `./tools/lint.js` - `Cargo.lock` resolves `deno_panic` 0.2.1 from crates.io; its sources are byte-identical to the branch commit previously pinned, and the `frame-pointer` cfg gating in `cli/lib.rs` matches the crate's own gating for `trace_frame_pointer` - `specs::test::recursive_permissions_pledge` — its `err.out` pinned the old panic-banner wording, which the Linux release build no longer emits; the sentence is now wildcarded and both wordings were checked against `wildcard_match_detailed` - full x86_64 Linux Deno release build with rebuilt std, forced frame pointers, and disabled unwind tables - native aarch64 Linux frame-pointer walker tests after stripping unwind sections - stripped Deno panic through a N-API fixture produced a 28-frame trace that symbolicated through Deno's panic hook, Rust std, the N-API call site, V8, `JsRuntime`, `MainWorker`, Tokio, and `main` - stripped `denort` compiled and ran a standalone program (`denort-fp-ok`) - stripped `libdenort.so` retained all 611 exports, loaded with `dlopen`, and was embedded successfully by the raw desktop backend; the embedded copy also loaded and contained no unwind sections - `readelf` checks confirm that `.eh_frame`, `.eh_frame_hdr`, and `PT_GNU_EH_FRAME` are absent from the packaged Linux artifacts
Author
Parents
Loading