tools/startup_order/README.md
Deno release CI arranges startup functions close together in the final executable. The generated linker order is specific to one binary and is created during that binary's release job.
Supported release targets:
x86_64-unknown-linux-gnuaarch64-unknown-linux-gnuaarch64-apple-darwinOther targets and non-release profiles use the standard linker configuration.
| File | Purpose |
|---|---|
generate_linux_function_orderfile.ts | Runs the Linux trace workloads and writes an LLD symbol order. |
orderfile_function_tracer_linux.c | Records exact Linux function entries with INT3 or BRK. |
generate_macos_function_orderfile.ts | Runs the macOS trace workloads and writes a Mach-O linker order. |
orderfile_function_tracer_macos.c | Records exact first function entries with arm64 BRK instructions. |
orderfile_trace_runner.c | Starts each workload while suspending the generator process. |
verify_orderfile.ts | Compares the baseline and ordered release binaries after linking. |
The linker integration is implemented in cli/build.rs. Release-job
orchestration is defined in .github/workflows/ci.ts and materialized in
.github/workflows/ci.generated.yml.
CI retains generated orders and reports as workflow artifacts for seven days.
For each supported release target, CI:
target/release/deno-before-startup-order.deno with the generated order.The trace workloads cover:
Deno.serve plus one request;deno test;node:crypto; anddeno fmt --check.Each generator creates temporary fixtures and a private DENO_DIR. The shared
native runner suspends the generator while a workload executes so the
generator's V8 threads cannot affect the trace.
Within one workload, entries from all three traces are combined in first-seen order. Functions already emitted by an earlier workload are not emitted again. Aliases at the same address are retained because the linker may expose multiple names for one function.
The Linux generator reads defined STT_FUNC entries with readelf and
linker-visible names with nm. The tracer:
PT_LOAD mappings into a memfd;INT3 or
arm64 BRK;SIGTRAP; andThe writable alias avoids a writable-executable mapping. Embedded V8 builtin blob entries are excluded because V8 copies those bytes to a separate executable mapping.
The resulting order is passed to LLD with --symbol-ordering-file.
The macOS generator reads LC_FUNCTION_STARTS and resolves linker-visible names
with llvm-nm. The tracer:
__text mapping;BRK;The resulting order is passed to the Apple linker with -order_file.
Build and preserve the baseline release executable:
unset DENO_USE_STARTUP_ORDER DENO_STARTUP_ORDER_FILE
DENO_SNAPSHOT_MINIFY_SOURCES=1 \
cargo build --release --locked -p deno --bin deno \
--features=deno/panic-trace
cp -p target/release/deno target/release/deno-before-startup-order
Generate the Linux order:
TARGET="$(uname -m)-unknown-linux-gnu"
ORDER="$PWD/target/release/startup-order-$TARGET.order"
target/release/deno run -A \
tools/startup_order/generate_linux_function_orderfile.ts \
--binary "$PWD/target/release/deno-before-startup-order" \
--output "$ORDER" \
--repeats 3 \
--workload-profile run-first
Generate the macOS order:
ORDER="$PWD/target/release/startup-order-aarch64-apple-darwin.order"
target/release/deno run -A \
tools/startup_order/generate_macos_function_orderfile.ts \
--binary "$PWD/target/release/deno-before-startup-order" \
--output "$ORDER" \
--repeats 3 \
--workload-profile run-first
Relink deno:
DENO_SNAPSHOT_MINIFY_SOURCES=1 \
DENO_USE_STARTUP_ORDER=1 \
DENO_STARTUP_ORDER_FILE="$ORDER" \
cargo build --release --locked -p deno --bin deno \
--features=deno/panic-trace
Verify the result before stripping the executable:
target/release/deno run -A tools/startup_order/verify_orderfile.ts \
--baseline-binary target/release/deno-before-startup-order \
--binary target/release/deno \
--order "$ORDER" \
--output "$ORDER.verify.json"
The build integration rejects an empty or implausibly small order. The verifier then checks:
The linked executable may expose fewer names because LTO and identical-code folding can select different aliases. Those missing names do not fail verification as long as enough common symbols remain for a meaningful comparison.
When the baseline executable already follows at least 90% of the requested sequence, it is treated as already ordered. In that case the final executable may remain unchanged and must not reduce conformance by more than two percentage points.
The report records the exact symbol count, missing names, longest nondecreasing address sequence, and conformance ratio for both executables, plus the direct comparison over their common symbols. Verification decisions use that baseline-relative comparison; other symbol counts and performance measurements remain telemetry.
CI retains these files for seven days:
startup-order-<target>.order — linker input;startup-order-<target>.order.json — workload and trace summary;startup-order-<target>.order.starts.json — Linux function-discovery summary;
andstartup-order-<target>.order.verify.json — post-link verification report.The current implementation produced these release-build results:
| Target | Ordered symbols | Timer-free RSS delta | Empty JS startup delta | Cached TypeScript startup delta |
|---|---|---|---|---|
| Linux x86-64 | 22,963 | -18,784 KiB | -1.897 ms | -2.009 ms |
| macOS arm64 | 46,869 | -10,960 KiB | -1.052 ms | -1.078 ms |
These measurements document expected behavior for engineering review. Changes to a tracer, generator, workload, linker configuration, or verifier should be evaluated with paired RSS and startup measurements.