d529c286
tools: valgrind recipe for the QUIC/C stack + first-run record
a73x 2026-08-14 09:29
Commit message
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -3205,3 +3205,9 @@ two dialects. | |||
| 3205 | zlint targets). The compiler's native strictness (unused locals/params, | 3205 | zlint targets). The compiler's native strictness (unused locals/params, |
| 3206 | shadowing) already owns the highest-value lint classes; re-try at the | 3206 | shadowing) already owns the highest-value lint classes; re-try at the |
| 3207 | next toolchain bump if its 0.16 support lands. | 3207 | next toolchain bump if its 0.16 support lands. |
| 3208 | - **valgrind first run (2026-08-14): pending install.** | ||
| 3209 | Recipe at tools/valgrind-quic.sh, non-gating (10-50x slowdown distorts every | ||
| 3210 | timing path). `sudo -n pacman -S --noconfirm valgrind` was refused with | ||
| 3211 | `sudo: a password is required` (exit 1): this box grants no non-interactive | ||
| 3212 | sudo, so the run half is pending install. valgrind not installable | ||
| 3213 | non-interactively; run `sudo pacman -S valgrind` then the script. | ||
tools/valgrind-quic.sh
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,46 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # tools/valgrind-quic.sh — periodic deep leak run over the QUIC/C stack. | ||
| 3 | # | ||
| 4 | # NON-GATING and wired into no build step, on purpose: valgrind runs | ||
| 5 | # 10-50x slow, which distorts every timing-sensitive path (handshakes, | ||
| 6 | # keepalives, settle windows) into flake territory. Its unique value is | ||
| 7 | # the C side — wolfSSL/ngtcp2 allocate with malloc, which the Zig | ||
| 8 | # DebugAllocator verdict (6a) never sees. Run it by hand after touching | ||
| 9 | # the QUIC stack or bumping deps/quic. | ||
| 10 | # | ||
| 11 | # Usage: tools/valgrind-quic.sh [path/to/muxd [path/to/muxa]] | ||
| 12 | # (defaults to zig-out/bin — run `zig build` first; Debug build is the | ||
| 13 | # point: Zig debug builds carry valgrind client requests natively) | ||
| 14 | set -eu | ||
| 15 | MUXD="${1:-zig-out/bin/muxd}" | ||
| 16 | MUXA="${2:-zig-out/bin/muxa}" | ||
| 17 | command -v valgrind >/dev/null || { echo "valgrind is not installed"; exit 1; } | ||
| 18 | [ -x "$MUXD" ] && [ -x "$MUXA" ] || { echo "binaries missing — run zig build"; exit 1; } | ||
| 19 | TMP="${TMPDIR:-/tmp}/mux-valgrind-$$" | ||
| 20 | mkdir -p "$TMP" | ||
| 21 | trap 'kill "$VGPID" 2>/dev/null; rm -rf "$TMP"' EXIT INT TERM | ||
| 22 | VGPID="" | ||
| 23 | # Hermetic key: keygen into a private XDG home, --key it explicitly. | ||
| 24 | XDG_CONFIG_HOME="$TMP/cfg"; export XDG_CONFIG_HOME | ||
| 25 | "$MUXD" keygen > /dev/null | ||
| 26 | KEY="$TMP/cfg/mux/key" | ||
| 27 | SOCK="$TMP/vg.sock" | ||
| 28 | PORT=$((47000 + ($$ % 900))) | ||
| 29 | valgrind --leak-check=full --error-exitcode=99 --log-file="$TMP/vg.log" \ | ||
| 30 | "$MUXD" run --sock "$SOCK" --shell /bin/sh \ | ||
| 31 | --quic "127.0.0.1:$PORT" --key "$KEY" & | ||
| 32 | VGPID=$! | ||
| 33 | # valgrind start is SLOW; give the bind a full minute. | ||
| 34 | i=0 | ||
| 35 | while [ ! -S "$SOCK" ] && [ "$i" -lt 600 ]; do sleep 0.1; i=$((i + 1)); done | ||
| 36 | [ -S "$SOCK" ] || { echo "daemon never bound under valgrind"; exit 1; } | ||
| 37 | # One real command over QUIC — handshake, frames, teardown — with a | ||
| 38 | # timeout sized for valgrind's clock, then a clean stop so every exit | ||
| 39 | # path (and the allocator teardown) runs. | ||
| 40 | "$MUXA" run --quic "127.0.0.1:$PORT" --key "$KEY" --timeout 60000 "echo vg-probe" \ | ||
| 41 | || echo "muxa run failed under valgrind (timing?) — the leak summary below still stands" | ||
| 42 | "$MUXD" stop --sock "$SOCK" | ||
| 43 | wait "$VGPID" || true | ||
| 44 | VGPID="" | ||
| 45 | echo "---- valgrind summary ----" | ||
| 46 | grep -E "definitely lost|indirectly lost|ERROR SUMMARY" "$TMP/vg.log" || cat "$TMP/vg.log" | ||