#!/usr/bin/env bash # Freeze autopsy for a LIVE (hung) Xenia-Canary process. # # When the game or the emulator freezes, DO NOT kill it. Run this instead: it # attaches gdb to the running process and dumps a backtrace of every thread, # which names exactly who is stuck and on what (the guest audio callback, a # kernel lock, the GPU present, an XMA context lock, ...). # # The Release binary is NOT stripped, so we get real function names. # # Usage: ./diagnose-freeze.sh [output_file] # Safe: read-only. gdb detaches afterwards and the process keeps running, so # you can dump twice and diff -- if two dumps 10s apart are identical, it is a # true deadlock, not slow progress. set -u OUT="${1:-/tmp/canary_freeze_$(date +%H%M%S).txt}" PID=$(pgrep -x xenia_canary | head -1) if [ -z "$PID" ]; then echo "No running xenia_canary process found (is it still up? don't kill it!)." exit 1 fi command -v gdb >/dev/null || { echo "ABORT: gdb not installed."; exit 4; } echo "Attaching to xenia_canary (pid $PID) -- read-only, it keeps running." { echo "=== xenia_canary freeze autopsy pid=$PID $(date) ===" echo echo "--- /proc/$PID/status ---" grep -E "^(State|Threads)" "/proc/$PID/status" 2>/dev/null echo echo "--- per-thread kernel wait channel (who is blocked, cheap) ---" for t in /proc/"$PID"/task/*; do tid=$(basename "$t") printf " tid %-7s state=%-2s wchan=%-24s %s\n" \ "$tid" \ "$(awk '{print $3}' "$t/stat" 2>/dev/null)" \ "$(cat "$t/wchan" 2>/dev/null || echo '-')" \ "$(cat "$t/comm" 2>/dev/null)" done echo echo "--- all thread backtraces (gdb) ---" } > "$OUT" gdb -p "$PID" -batch \ -ex "set pagination off" \ -ex "set confirm off" \ -ex "thread apply all bt" \ -ex "detach" 2>&1 | tee -a "$OUT" > /dev/null echo "Wrote: $OUT" echo echo "--- threads that look blocked ---" grep -E "^Thread |pthread_cond_wait|futex|__lll_lock|Wait|Acquire" "$OUT" | head -40 echo echo "Full dump: $OUT (run again in ~10s and diff to confirm a true deadlock)"