summaryrefslogtreecommitdiff
path: root/src/fuzz_test.sh
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-18 18:17:39 -0700
committerScott Gasch <[email protected]>2026-09-18 18:17:39 -0700
commit284113f878444ded9cd36b4a70900efd036651de (patch)
treef527e1e96ead13a4805d52ca3072260db46ee35e /src/fuzz_test.sh
parentf698c02de213d0ed8968fb7cca5f70df51fa0763 (diff)
Add fuzz_test.sh: a long-running gdb-driven fuzz loop over cross_sectional_200k.ep_
Documents two build/invocation bugs hit getting this working: DEBUG builds need MP=1 too or --cpus is silently unrecognized, and gdb's "handle SIGTRAP nostop noprint pass" breaks thread control against the MP/pthread binary (misdiagnosed at first as a sandboxed ptrace restriction, but reproduces outside any harness and goes away once that directive is dropped). Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/fuzz_test.sh')
-rwxr-xr-xsrc/fuzz_test.sh73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/fuzz_test.sh b/src/fuzz_test.sh
new file mode 100755
index 0000000..5e3d5f7
--- /dev/null
+++ b/src/fuzz_test.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Long-running fuzz loop per TESTING_PLAN.md's "Fuzz testing" section:
+# repeatedly draw a fresh random batch of setboard/go records from the full
+# 200k-position tests/cross_sectional_200k.ep_ pool, run them through a
+# DEBUG=1 build under gdb (so any assert/SIGTRAP/SIGSEGV leaves a full
+# backtrace instead of just a bare crash), shallow-depth, small/no hash
+# (setboard memsets the hash table as a side effect, so a large --hash
+# wastes time on every single record), --cpus 2 to exercise split.c's
+# parallel search path too. Runs until stopped (^C) or a crash is found.
+#
+# Usage: ./fuzz_test.sh [binary] [depth] [batch_size] [cpus]
+
+BIN="${1:-./_typhoon}"
+DEPTH="${2:-6}"
+BATCH="${3:-200}"
+CPUS="${4:-2}"
+SUITE=../tests/cross_sectional_200k.ep_
+LOGDIR=/usr/local/tmp/typhoon/fuzz
+mkdir -p "$LOGDIR"
+
+if [ ! -x "$BIN" ]; then
+ echo "ERROR: $BIN not found/executable (build first: gmake clean; gmake DEBUG=1 MP=1)"
+ exit 1
+fi
+if [ ! -f "$SUITE" ]; then
+ echo "ERROR: $SUITE not found (run from src/)"
+ exit 1
+fi
+
+TOTAL_RECORDS=$(grep -c '^go' "$SUITE")
+
+ITER=0
+while :; do
+ ITER=$((ITER + 1))
+ SAMPLE="$LOGDIR/batch_${ITER}.ep_"
+ awk -v k="$BATCH" -v n="$TOTAL_RECORDS" 'BEGIN { srand() }
+ { sub(/\r$/, "") }
+ /^setboard/ { rec = $0; next }
+ /^go[ \t]*$/ { rec = rec "\n" $0; c++; recs[c] = rec; rec = ""; next }
+ rec != "" { rec = rec "\n" $0 }
+ END {
+ picked = 0
+ while (picked < k && picked < c) {
+ r = int(rand() * c) + 1
+ if (!(r in used)) { used[r] = 1; print recs[r]; picked++ }
+ }
+ }' "$SUITE" > "$SAMPLE"
+
+ STAMP=$(date +%Y%m%d_%H%M%S)
+ LOG="$LOGDIR/run_${ITER}_${STAMP}.log"
+ GDBOUT="$LOGDIR/run_${ITER}_${STAMP}.gdb.txt"
+
+ echo "=== iter $ITER: $BATCH positions from $SUITE (sd $DEPTH, cpus $CPUS) -> $GDBOUT ==="
+
+ gdb -q -batch \
+ -ex "set pagination off" \
+ -ex run \
+ -ex "bt full" \
+ -ex "info registers" \
+ --args "$BIN" --cpus "$CPUS" --hash 1m --logfile "$LOG" \
+ --batch --command "force; book name /nonexistent.book.bin; sd $DEPTH; script $SAMPLE" \
+ > "$GDBOUT" 2>&1
+ STATUS=$?
+
+ if grep -qE 'Program terminated with signal (SIGSEGV|SIGABRT|SIGBUS|SIGILL|SIGFPE)|Assertion failure' "$GDBOUT"; then
+ echo "*** CRASH/ASSERT DETECTED on iter $ITER -- see $GDBOUT ***"
+ echo " positions: $SAMPLE"
+ echo " engine log: $LOG"
+ exit 1
+ fi
+
+ echo " iter $ITER clean (exit=$STATUS)"
+done