diff --git a/docs/re/captures/select-data-reached-no-crash.png b/docs/re/captures/select-data-reached-no-crash.png new file mode 100644 index 00000000..42503952 Binary files /dev/null and b/docs/re/captures/select-data-reached-no-crash.png differ diff --git a/docs/re/title-crash-stl-tree.md b/docs/re/title-crash-stl-tree.md index a8566ac8..1142faac 100644 --- a/docs/re/title-crash-stl-tree.md +++ b/docs/re/title-crash-stl-tree.md @@ -222,3 +222,37 @@ Not settled: whether a warm cache prevents *this* firing the way it prevents the boot-time one. The cache was warm here (the 6-file `aab216c3` restored earlier), so the answer looks like **no** — but that is one run, and the cold/warm A/B was only ever run against the boot-time throw. + +## Standing blocker: the mission path, measured end to end (2026-08-19) + +Driving `menu → NEW GAME → DIFFICULTY → SELECT DATA → pick slot 01` with plain +flags (`--mem_watch=false`, no EH knobs) gets **further than any run so far** and +still ends the same way: + +| step | outcome | +|---|---| +| main menu → NEW GAME | **DIFFICULTY** | +| DIFFICULTY → Ⓐ | **SELECT DATA**, and this time with **0 crashes** ([capture](captures/select-data-reached-no-crash.png)) — the screen is alive, a `log_ui_draws` probe there records **140 draws over 8 frames** | +| slot 01 → Ⓐ | the game proceeds — several changing frames, a cinematic or load — and then **crashes at `0x82307128`**, the same cache-flush `std::map` erase | + +Two things this settles, and one it does not. + +**Settled: the crash is intermittent in *where* it fires, not whether.** It has +now been seen at boot, at `SELECT DATA`, and after the save slot is chosen. The +same run reached `SELECT DATA` cleanly and died one screen later. So there is no +"safe path" through the menus to be found by picking different options — the +flush throws whenever it next runs. + +**Settled: this is the blocker for every mission-side experiment.** The second +capital-ship capture, in-flight probes, mission-outcome work: all of them are +behind this, and navigation is no longer the obstacle — that part is scripted and +works (`tools/re-capture/newgame_path.sh`, `blackscreen_probe.sh`). + +**Not settled: how to get past it.** `--mem_watch=false` does not (measured +twice). `--eh_dispatch` remains untested because no run with it on has reached a +throw. And the black-screen hang is a *separate* intermittent failure that takes +some runs out earlier — it is not the crash, and it has no diagnosis yet. + +The cheap trigger from the top of this note (an incomplete on-disc cache) still +stands as the fastest way to reproduce the throw for bisection; what is missing +is a fix, and that is guest-race work in the emulator, not RE. diff --git a/tools/re-capture/blackscreen_probe.sh b/tools/re-capture/blackscreen_probe.sh new file mode 100755 index 00000000..7adf8ed6 --- /dev/null +++ b/tools/re-capture/blackscreen_probe.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Drive into the black-screen hang and ask what the guest is still doing. +# +# Three questions, three cheap probes, all taken WHILE the screen is black: +# * is it still presenting frames? -> F10 arms the UI draw capture, whose +# header records the frame numbers it covered and how many draws it saw +# * is it still doing file I/O? -> count ResolvePath lines before/after +# * is it still running at all? -> the emulator's own MEM-WATCH/thread log +set -u +export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 +SD="$(cd "$(dirname "$0")" && pwd)" +OUT="${1:-/sylph-home/re/blackprobe}" +mkdir -p "$OUT"; rm -f "$OUT"/xenia_re_ui_draws_*.log +shot(){ screenshot "$1" >/dev/null 2>&1; } +screen(){ shot /tmp/bp.png; python3 "$SD/screen_id.py" /tmp/bp.png | awk '{print $1}'; } +alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; } +resolves(){ grep -c "ResolvePath" "$OUT/canary.stdout" 2>/dev/null || echo 0; } + +for attempt in $(seq 1 "${ATTEMPTS:-3}"); do +echo "=== attempt $attempt" +pkill -9 -x xenia_canary 2>/dev/null; sleep 3 +( cd "$OUT" && nohup run-canary --mem_watch=false \ + --ui_draw_capture_frames=8 --ui_draw_capture_max=4000 \ + --logged_profile_slot_0_xuid=B13EBABEBABEBABE >"$OUT/canary.stdout" 2>&1 & ) +sleep 8 +until xdotool search --name "Xenia-canary" >/dev/null 2>&1; do + [ -n "$(alive)" ] || { echo "EMULATOR GONE"; exit 4; }; sleep 1 +done +win="$(xdotool search --name "Xenia-canary" | tail -1)" + +s=""; deadline=$(( SECONDS + 420 )) +while [ $SECONDS -lt $deadline ]; do s="$(screen)"; [ "$s" = "title" ] && break; sleep 2; done +[ "$s" = "title" ] || { echo "NO TITLE"; continue; } +python3 "$SD/pad.py" tap A 0.3 +for _ in 1 2 3 4 5 6 7 8; do sleep 3; s="$(screen)"; [ "$s" = "menu" ] && break; done +[ "$s" = "menu" ] || { echo "(A) refused; rebooting"; continue; } +echo "menu -> A (NEW GAME) -> A (difficulty)" +python3 "$SD/pad.py" tap A 0.3; sleep 10 +python3 "$SD/pad.py" tap A 0.3 + +# wait for black +for i in $(seq 1 20); do + sleep 6; s="$(screen)" + mean=$(identify -format "%[fx:mean]" /tmp/bp.png 2>/dev/null) + echo " t+$((i*6))s $s mean=$mean" + case "$mean" in 0|0.0*) break ;; esac +done +shot "$OUT/black.png" +echo "--- probing the black screen" +r1=$(resolves); sleep 20; r2=$(resolves) +echo "ResolvePath lines: $r1 -> $r2 (delta $((r2-r1)))" +xdotool windowactivate --sync "$win"; sleep 1 +xdotool key F10; sleep 4; xdotool mousemove 900 400 click 1; sleep 3 +grep -i "UI-CAP" "$OUT/canary.stdout" | tail -3 +head -2 "$OUT"/xenia_re_ui_draws_*.log 2>/dev/null +echo "log lines now: $(wc -l < "$OUT/canary.stdout")" +sleep 20 +echo "log lines +20s: $(wc -l < "$OUT/canary.stdout")" +echo "BLACK PROBE DONE" +exit 0 +done +echo "GAVE UP"