The gdb route works, and the recipe is written down: run-canary execs $XENIA_BIN, so a wrapper that execs "gdb --args <real binary>" keeps the lockfile, the flags and the process name (gdb forks and execs the real binary, so ps -C xenia_canary still finds the inferior) while satisfying ptrace_scope=1 by being the parent. The Release binary is not stripped - 26595 symtab entries - so frames have names. The handle SIGSEGV/SIGBUS/SIG32-35 lines are mandatory: xenia uses SIGSEGV for guest memory watches and the RT signals for thread suspend. A run froze after ~4 minutes of flight, screen still "flight" rather than GAME OVER, and all 79 threads had backtraces. EVERY ONE is in a wait - guest threads in KeWaitForSingleObject / NtWaitForSingleObjectEx / SelfSuspend, the GPU command processor parked idle, the main thread in poll(). It is nevertheless burning 1253 ticks per 10 s: 403 in the TimerQueue thread (nanosleep inside TimerThreadMain) and 290 + 274 in two guest threads that the backtrace shows blocked in KeWaitForSingleObject. A thread genuinely blocked cannot burn 28% of a core, so those two are CYCLING - a timed wait that expires and is re-entered - with the timer thread servicing them hot. Three samples minutes apart show identical frames. That refines the earlier "the guest is spinning, not deadlocked": the CPU burn is real but it is in the WAIT PATH inside the kernel layer, not in guest code. The shape is an event that never gets signalled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
24 lines
1.0 KiB
Bash
Executable File
24 lines
1.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Take a backtrace of every thread of an emulator that is running under gdb.
|
|
#
|
|
# Pair with the wrapper at /sylph-home/re/bin/gdb-wrap/xenia_canary (see
|
|
# docs/re/mission-freeze-resume-spin.md): ptrace_scope=1 forbids attaching to a
|
|
# running emulator, so it has to be STARTED under gdb.
|
|
#
|
|
# SIGINT to gdb stops the inferior; commands then go in through the command file
|
|
# the wrapper is tailing, and the answer comes back in the gdb log. `continue` at
|
|
# the end so the run can carry on.
|
|
set -u
|
|
CMD="${GDB_CMD_FILE:-/tmp/gdb-cmd}"
|
|
OUT="${GDB_OUT_FILE:-/tmp/gdb-out.log}"
|
|
DEPTH="${1:-12}"
|
|
pid=$(pgrep -x gdb | head -1)
|
|
[ -n "$pid" ] || { echo "no gdb process — was the emulator started with XENIA_BIN=/sylph-home/re/bin/gdb-wrap/xenia_canary ?"; exit 1; }
|
|
mark="=== BT $(date +%s) ==="
|
|
before=$(wc -c < "$OUT")
|
|
kill -INT "$pid"
|
|
sleep 3
|
|
{ echo "echo $mark\\n"; echo "info threads"; echo "thread apply all bt $DEPTH"; echo "echo === END ===\\n"; echo "continue"; } >> "$CMD"
|
|
sleep 8
|
|
tail -c +$((before + 1)) "$OUT"
|