This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/tools/re-capture/freeze_watch.sh
Sylpheed RE agent 5fb4da4c43 docs+tools: the call-rate probe's baseline already refutes the timeout reading
v2 counts every KeWaitForSingleObject call per thread per second, tracks how many
distinct objects it saw, and logs the last result. Its HEALTHY baseline is a
result on its own: over a full 22-minute run the main thread cleared 500 calls/s
in 224 separate windows, peaking at 1235 calls/s over up to THIRTEEN distinct
objects, and the last result was X_STATUS_SUCCESS in all 314 windows. Not one
timeout.

So the game normally does hundreds of successful waits a second across many
objects - exactly the blind spot v1 could not see, and the reason a timeout-streak
counter reported the same single poller whether the game was frozen or healthy.

Stated as a consequence rather than a triumph: 500/s is NOT self-selecting, since
the main thread clears it constantly, so the freeze signal has to be a different
shape - a thread far above 1235/s, a new thread, or a window whose result is not
SUCCESS. That still needs a frozen sample; run 5 ended in GAME OVER at ~22 min
without freezing.

freeze_watch.sh now summarises the rate probe per thread when it captures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-24 08:14:11 +00:00

58 lines
2.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Wait for the in-mission freeze and capture the evidence at the moment it lands.
#
# Written because catching it by hand costs a tool call every 25 s and the freeze
# takes anywhere from ten seconds to never: roughly half the runs that reach
# flight freeze, and the other half do not
# (docs/re/mission-freeze-resume-spin.md).
#
# "Frozen" is two screenshots byte-identical (frozen.py); it is checked ONLY
# while the flight HUD is still on screen, because the GAME OVER screen animates
# and a run that ended there is not a freeze.
#
# On detection it snapshots what the stuck-wait probe has said, so the comparison
# against the healthy-run baseline (one thread polling one Event) is made from
# the same instant rather than reconstructed later.
set -u
export HOME=/sylph-home/re
DISP="${DISPLAY:-:98}"
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG="${1:-/sylph-home/re/shots/probe-canary.stdout}"
OUT="${2:-/sylph-home/re/logs/freeze-evidence.txt}"
DEADLINE=$(( SECONDS + ${3:-1800} ))
: > "$OUT"
while [ $SECONDS -lt $DEADLINE ]; do
if ! pgrep -x xenia_canary >/dev/null; then
echo "EMULATOR GONE at ${SECONDS}s" | tee -a "$OUT"; exit 4
fi
if python3 "$SD/frozen.py" 5 >/dev/null; then
if python3 -c "import sys; sys.path.insert(0,'$SD'); import frozen; sys.exit(0 if frozen.in_flight() else 1)"; then
{
echo "FROZEN IN FLIGHT at ${SECONDS}s"
echo
echo "=== stuck-wait probe: (thread, object) pairs ==="
grep -oE 'thread [0-9A-F]+ has timed out [0-9]+ times in a row on object [0-9A-F]+ \(type [0-9]+\)' "$LOG" \
| sed -E 's/has timed out [0-9]+ times in a row //' | sort | uniq -c | sort -rn
echo
echo "=== last 12 stuck-wait lines ==="
grep 'wait-probe' "$LOG" | tail -12
echo
echo "=== call-rate probe: per-thread rates in the last 20 windows ==="
grep 'wait-rate' "$LOG" | tail -20 \
| sed -E 's/.*thread ([0-9A-F]+) called KeWaitForSingleObject ([0-9]+) times in ([0-9]+) ms over ([0-9]+) distinct.*last result ([0-9A-F]+)/thread \1 \2 calls\/\3ms \4 objects result \5/'
echo
echo "=== call-rate: highest single window per thread, whole run ==="
grep 'wait-rate' "$LOG" \
| sed -E 's/.*thread ([0-9A-F]+) called KeWaitForSingleObject ([0-9]+) times.*/\1 \2/' \
| sort -k1,1 -k2,2nr | awk '!seen[$1]++ {print " thread " $1 " peak " $2 " calls/window"}'
} | tee -a "$OUT"
exit 0
fi
echo "frozen but NOT in flight (mission over) at ${SECONDS}s" | tee -a "$OUT"
exit 5
fi
sleep 20
done
echo "NO FREEZE within ${3:-1800}s" | tee -a "$OUT"; exit 1