The withdrawal last iteration was caused by `rm -f /tmp/xenia-canary.lock` -- the obvious way past a lock orphaned by kill -9, which also disables the guard for every later launch. Three instances ended up live at once, sharing /tmp/xenia_pad.txt and display :98, and silently confounded an input experiment. Care is not a fix, so this is tooling. ensure_single_emulator.sh counts live instances, stops them (plain kill, then -9, each with a bounded wait), verifies ZERO, and only then removes the lock -- refusing loudly if any remain. The lock is never removed before the condition it guards against is verified absent. FOUR scripts did the bare `rm -f`, and only two were mine from today: menu_loop_session.sh, title_draw_capture.sh, poke_control.sh and resume_reliability.sh. So the footgun was corpus-wide rather than introduced this session. All four now route through the guard. The guard is controlled rather than assumed: run against a deliberately started live instance it reports "1 instance(s) live -- stopping them", ends at 0 with the lock cleared, and exits 0. A guard that only ever passes on an already-clean slate would prove nothing. It also kills by process NAME. `pkill -f xenia_canary` matches the shell running it -- that has now cost this corpus three commands, one of them a cleanup that died halfway and left the very instances it was meant to remove. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
59 lines
2.7 KiB
Bash
Executable File
59 lines
2.7 KiB
Bash
Executable File
#!/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"
|
|
|
|
# Refuse to run two of these at once. Each iteration below kills the emulator by
|
|
# name, which is outside run-canary's lockfile — so a second copy of this script
|
|
# silently killed the first copy's boots mid-run and they were recorded as
|
|
# failures ("title_at_s=none", the emulator log ending in "Killed"). One
|
|
# emulator at a time has to mean one DRIVER at a time too.
|
|
exec 8>/tmp/resume-reliability.lock
|
|
if ! flock -n 8; then
|
|
echo "resume_reliability.sh: another run is in progress (lock /tmp/resume-reliability.lock)" >&2
|
|
echo " kill it with: pkill -x resume_reliability.sh # -x, NOT -f: -f matches your own shell" >&2
|
|
exit 1
|
|
fi
|
|
|
|
for i in $(seq 1 "$RUNS"); do
|
|
pkill -9 -x xenia_canary 2>/dev/null; sleep 3
|
|
. "$(dirname "${BASH_SOURCE[0]}")/ensure_single_emulator.sh"
|
|
ensure_single_emulator || exit 3
|
|
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"
|