Resuming happens on the other machine, so anything that lived only in this workspace either comes into the repo or gets named as something to carry. **The launchers come in.** `run-canary-safe.sh` (Wine) and `run-canary-native-safe.sh` (native) are the ONLY sanctioned way to start Canary from the editor — they force software Vulkan and refuse to launch while a hardware Vulkan device is visible, because Canary on the AMD GPU takes VS Code with it. They sat in the workspace root, outside git, hardcoding one machine's absolute paths, which is exactly why they could not be checked in. They now derive the workspace from their own location and honour `$SYLPHEED_ISO` and `$CANARY_BIN`, so the layout is a default rather than a requirement. Same for `run-canary-native.sh` (interactive, hardware Vulkan — not from the IDE), `diagnose-freeze.sh`, `live-guest-state.sh`, `heaptrack-wrap.sh` and the `asound-null.conf` the native launcher needs beside it. Both safe launchers were run from `tools/` before this commit: the native one reached content in 20 s (`VERDICT: HEALTHY`, title + 25,398 XMA lines, no faults), the Wine one ran 25 s and logged 4 `ADV.wmv` hits. **`docs/agents/HANDOFF-2026-09-18.md`** records what changed since 2026-09-16, what is on the server, what cannot travel through git, and the traps this machine paid for — chiefly that the guest-PC branch probe exists only on the fork's snapshot branch, so a rebuild from `sylpheed-re` silently loses it. **`HANDOFF-2026-09-06.md`** gets a correction note rather than an edit: a plain clone works again now that the 545 MB branch is gone, and its baselines are two baselines old. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
59 lines
2.0 KiB
Bash
Executable File
59 lines
2.0 KiB
Bash
Executable File
#!/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)"
|