Files
Sylpheed/tools/re-capture/resume_reliability.sh
sylph-decoder 4c92f54439 re: make the one-emulator rule enforceable instead of remembered
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
2026-08-30 16:23:59 +00:00

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"