tools: a reliability harness, and a title test that also requires a static frame
resume_reliability.sh boots N times and records, per boot, whether the title's
loader thread was created and whether it ever became the CALLER of a kernel call.
That is the thing the threading fix targets, and it is binary; "did the menu
appear" is a worse test because it needs the game to get further and the attract
loop confuses it.
Two defects fixed on the way, both found by the harness disagreeing with itself:
* is_title.py's glyph count alone still fired at 113s and 173s, during the
intro movie, and the single press was wasted there. Measured, not guessed:
on those runs NO loader thread was created at all, and the title handler
always creates one - even on the boots where it then fails to run. The title
check now also requires the frame to be STATIC, reusing the RMSE between the
two grabs 0.6s apart that the movie branch already computes, and raises the
glyph threshold to 800 (a real title measures ~1450-1520, the SQUARE ENIX
logo 0).
* `grep -ac ... || echo 0` corrupted every CSV field it guarded: grep exits 1
on zero matches, so the fallback APPENDED a second value and the field came
out "0 0".
Retrospective before/after from the logs already on disc, using ResolvePath after
the resume (logged at every verbosity, so it is comparable across all runs):
before the fix 1 of 5 boots that created a loader thread went on to load assets;
after it, 2 of 2. n is small and a proper count is running.
This commit is contained in:
45
tools/re-capture/resume_reliability.sh
Executable file
45
tools/re-capture/resume_reliability.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Count how often the title screen's loader thread actually RUNS.
|
||||
#
|
||||
# Pressing (A) at the title makes the game ExCreateThread(CREATE_SUSPENDED) +
|
||||
# NtResumeThread. Before canary a60fe7d11 that resume could be lost, leaving a
|
||||
# thread with zero kernel calls and zero CPU (docs/re/canary-scripted-input-traps.md).
|
||||
# The menu appearing is a poor test — it needs the game to get further, and the
|
||||
# attract loop confuses it — so this measures the thing the fix targets directly:
|
||||
# does the slot-(1F) thread appear as the CALLER of any kernel call?
|
||||
#
|
||||
# Needs kernel logging, which is off by default: LOG_MASK=12 LOG_LEVEL=3.
|
||||
#
|
||||
# resume_reliability.sh [runs] [tag]
|
||||
set -u
|
||||
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
||||
SD="$(cd "$(dirname "$0")" && pwd)"
|
||||
RUNS="${1:-4}"
|
||||
TAG="${2:-rel}"
|
||||
OUT=/sylph-home/re/resume-reliability
|
||||
mkdir -p "$OUT"
|
||||
CSV="$OUT/$TAG.csv"
|
||||
echo "run,title_at_s,thread_created,thread_ran_calls,resolvepath_after,screen" > "$CSV"
|
||||
|
||||
for i in $(seq 1 "$RUNS"); do
|
||||
pkill -9 -x xenia_canary 2>/dev/null; sleep 3
|
||||
rm -f /tmp/xenia-canary.lock
|
||||
LOG_MASK=12 LOG_LEVEL=3 BOOT_MENU_LOG="$OUT/$TAG-$i.stdout" \
|
||||
timeout 700 "$SD/boot_menu.sh" "$TAG-$i" > "$OUT/$TAG-$i.boot" 2>&1
|
||||
sleep 20
|
||||
L="$OUT/$TAG-$i.stdout"
|
||||
t=$(grep -ao 'TITLE at [0-9]*s' "$OUT/$TAG-$i.boot" 2>/dev/null | head -1 | tr -dc '0-9')
|
||||
created=$(grep -ac '(1F) Stack' "$L" 2>/dev/null); created=${created:-0}
|
||||
h=$(grep -a '(1F) Stack' "$L" 2>/dev/null | tail -1 | grep -ao 'XThread[0-9A-F]*' | sed 's/XThread//')
|
||||
calls=0; rp=0
|
||||
if [ -n "${h:-}" ]; then
|
||||
calls=$(grep -ac "^[dik]> $h" "$L" 2>/dev/null); calls=${calls:-0}
|
||||
n=$(grep -an "NtResumeThread($h" "$L" 2>/dev/null | tail -1 | cut -d: -f1)
|
||||
if [ -n "${n:-}" ]; then rp=$(tail -n +"$n" "$L" | grep -ac 'ResolvePath'); rp=${rp:-0}; fi
|
||||
fi
|
||||
screenshot "$OUT/$TAG-$i.png" >/dev/null 2>&1
|
||||
s=$(python3 "$SD/screen_id.py" "$OUT/$TAG-$i.png" 2>/dev/null | awk '{print $1}')
|
||||
echo "$i,${t:-none},$created,$calls,$rp,${s:-none}" | tee -a "$CSV"
|
||||
done
|
||||
pkill -9 -x xenia_canary 2>/dev/null
|
||||
echo "--- $CSV ---"; cat "$CSV"
|
||||
@@ -50,7 +50,12 @@ while [ $SECONDS -lt $deadline ]; do
|
||||
# then over-matched the other way: it called the SQUARE ENIX publisher logo
|
||||
# "title" 151s into a boot, and this script spent its one press there.
|
||||
# is_title.py counts glyph pixels, which only the interactive title has.
|
||||
if python3 "$SD_SI/is_title.py" /tmp/f2.png >/dev/null 2>&1; then
|
||||
# AND the frame must be STATIC. A glyph count alone still fired at 113-173s,
|
||||
# during the intro movie, and the press was wasted — measured by the loader
|
||||
# thread that the title handler ALWAYS creates being absent entirely on those
|
||||
# runs. The resting title barely changes between two grabs 0.6s apart; a movie
|
||||
# does, and `$d` is already computed above for exactly that distinction.
|
||||
if [ "$d" -le 1500 ] && python3 "$SD_SI/is_title.py" /tmp/f2.png 800 >/dev/null 2>&1; then
|
||||
echo "TITLE at ${SECONDS}s -> A"; tapA; exit 0
|
||||
fi
|
||||
# DO NOT tap through the movies. This branch used to, and for a long time it
|
||||
|
||||
Reference in New Issue
Block a user