makepad/tools/run/fixture/settle_stress.sh

115 lines
6.2 KiB
Bash
Executable file

#!/bin/sh
# Stress test of compiler memory across settles (MEMSAFE.DESIGN.md; replaces zone_swap.sh/zone_stress.sh).
# Memory is freed only by its owner's Drop, never in bulk, so this checks the runtime side of that
# rule under the worst interleaving we can force: each package starts hidden and muted (cold cache
# of its own) with
# MAKEPAD_RUN_SETTLE_STRESS=$PERIOD a settle (settle_drop, retire, trim) every PERIOD ms while
# compiles, O3 bursts and speculation run,
# MAKEPAD_RUN_ZAP=1 every freed compiler / job-arena block filled with 0xcd,
# MAKEPAD_RUN_JOB_CHECK=1 a line for every job that left blocks in its job arena.
# Probes across the settles (the 2026-10-20 P0: inspect's records and a counted fn): `count $PROBE` is
# installed, removed and installed again three times before the first settle; after RUNS settles the
# counted fn must keep counting, and rm + count again must work. Then a clean quit (exit status 0).
# The output may hold no fault, panic, abort or POISONED line; job-arena survivors are reported (they
# cost memory, not safety) and fail the run only with STRICT_JOBS=1.
# Run from the workspace root after `cargo build --release -p makepad-run` and `-p makepad-remote`.
# PACKAGES (default "makepad-app-calculator"), PERIOD (ms, default 300), AFTER (seconds of stress after
# the first settle, default 8), WAIT (seconds to wait for the first frame / settle, default 90), TARGET
# (default target/release). A run passes only with at least one settle rebuild (NEED_REBUILD=0: off).
# Logs stay in a scratch directory under TMPDIR (removed on success unless KEEP=1).
T=${TARGET:-target/release}
PACKAGES=${PACKAGES:-makepad-app-calculator}
if [ ! -x "$T/makepad-remote" ]; then
echo "settle_stress: FAIL: $T/makepad-remote is missing (cargo build --release -p makepad-remote): the probes cannot be driven"
exit 2
fi
PERIOD=${PERIOD:-300}
AFTER=${AFTER:-8}
WAIT=${WAIT:-90}
PROBE=${PROBE:-makepad_platform::remote::imp::path_dump}
DRIVE=${DRIVE:-dump paths=1}
dir=$(mktemp -d "${TMPDIR:-/tmp}/settle_stress.XXXXXX") || exit 1
fails=0
k=0
for pkg in $PACKAGES; do
d=$dir/$pkg
k=$((k + 1)); td=$dir/t$k
mkdir -p "$d/home" "$td"
MAKEPAD_HIDE_WINDOWS=1 MAKEPAD_MUTE=1 SANDBOX_MUTE=1 MUSIC_MUTE=1 MAKEPAD_REMOTE=1 MAKEPAD_HOME="$d/home" TMPDIR="$td" \
MAKEPAD_RUN_HOME="$d/runhome" MAKEPAD_RUN_CACHE_DIR="$d/cache" MAKEPAD_WINDOW=1280x800@1 MAKEPAD_RUN_VERBOSE=1 \
MAKEPAD_RUN_SETTLE_STRESS="$PERIOD" MAKEPAD_RUN_ZAP=1 MAKEPAD_RUN_JOB_CHECK=1 \
"$T/makepad" run "$pkg" > "$d/run.log" 2>&1 &
pid=$!
: > "$d/fixture.log"
ask() { TMPDIR="$td" "$T/makepad" inspect $pid "$@" as=json >> "$d/probes.log" 2>&1; }
drive() { for r in 1 2 3; do TMPDIR="$td" "$T/makepad-remote" app $pid $DRIVE > /dev/null 2>&1; done; }
hits() { TMPDIR="$td" "$T/makepad" inspect $pid probes as=json 2>/dev/null | grep -o '"hits":[0-9]*' | head -1 | cut -d: -f2; }
settles() { grep -ac "makepad-run mem: settle at" "$d/run.log"; }
n=0
until TMPDIR="$td" "$T/makepad" inspect $pid ping > /dev/null 2>&1 && grep -q "first frame at" "$d/run.log"; do
n=$((n + 1)); [ $n -ge $((WAIT * 5)) ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.2
done
drive
for r in 1 2 3; do ask count "$PROBE"; drive; ask rm all; done
ask count "$PROBE"
n=0
until [ "$(settles)" -gt 0 ]; do
n=$((n + 1)); [ $n -ge $((WAIT * 5)) ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.2
done
s0=$(settles)
if [ "$s0" -gt 0 ]; then
h0=$(hits)
i=0
while [ $i -lt "$AFTER" ]; do drive; sleep 1; i=$((i + 1)); done
# and the first settle rebuild (it waits for the run's mid-run collected write)
n=0
until grep -aq "makepad-run settle rebuild at" "$d/run.log"; do
n=$((n + 1)); [ $n -ge $((WAIT * 5)) ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.2
done
i=0
while [ $i -lt 3 ]; do drive; sleep 1; i=$((i + 1)); done
TMPDIR="$td" "$T/makepad" inspect $pid memory > "$d/memory.log" 2>&1
h1=$(hits)
ask rm all
ask count "$PROBE"
drive
h2=$(hits)
s1=$(settles)
echo "settle_stress: settles ${s0} -> ${s1}; probe hits ${h0:-none} -> ${h1:-none}; reinstalled: ${h2:-none}" >> "$d/fixture.log"
[ -n "$h0" ] && [ -n "$h1" ] && [ "$h1" -gt "$h0" ] || echo "settle_stress: $pkg: the counted fn stopped counting across the settles" >> "$d/fixture.log"
[ -n "$h2" ] && [ "$h2" -gt 0 ] || echo "settle_stress: $pkg: count after the settles failed" >> "$d/fixture.log"
[ "$s1" -gt "$s0" ] || echo "settle_stress: $pkg: no settle under stress after the first" >> "$d/fixture.log"
else
echo "settle_stress: $pkg: no settle within ${WAIT} s" >> "$d/fixture.log"
fi
alive=1
kill -0 $pid 2>/dev/null || alive=0
[ $alive = 1 ] && kill -TERM $pid
wait $pid
rc=$?
why=
[ $alive = 0 ] && why="$why died under the stress (status $rc);"
[ $alive = 1 ] && [ $rc -ne 0 ] && why="$why exit status $rc;"
fx=$(grep -aE "no settle|stopped counting|failed" "$d/fixture.log" | head -1)
[ -n "$fx" ] && why="$why $fx;"
bad=$(grep -aE "makepad-run: fault|fault (at|in) |panicked|Segmentation|Bus error|SIGSEGV|SIGBUS|SIGILL|Aborted|POISONED|run monitor ended|state may be corrupt" "$d/run.log" | head -2)
[ -n "$bad" ] && why="$why $bad;"
jobs=$(grep -ac "^makepad-run job:" "$d/run.log")
rebuilds=$(grep -ac "makepad-run settle rebuild at" "$d/run.log")
[ "${NEED_REBUILD:-1}" = 1 ] && [ "$rebuilds" -eq 0 ] && why="$why no settle rebuild ($(grep -a 'settle rebuild deferred\|settle rebuild refused' "$d/run.log" | tail -1 | cut -c1-160));"
[ "${STRICT_JOBS:-0}" = 1 ] && [ "$jobs" -gt 0 ] && why="$why $jobs jobs left survivors;"
if [ -z "$why" ]; then
echo "settle_stress: $pkg: ok ($(grep -a 'settles' "$d/fixture.log" | cut -c16-); $rebuilds settle rebuilds; $jobs jobs left survivors)"
else
echo "settle_stress: $pkg: FAIL:$why (logs $d/run.log, $d/fixture.log)"
fails=$((fails + 1))
fi
done
[ $fails -eq 0 ] && [ "${KEEP:-0}" != 1 ] && rm -rf "$dir"
exit $fails