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>
73 lines
3.2 KiB
Bash
Executable File
73 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Read the GUEST state of a live (hung) xenia_canary — who is the guest spinning
|
|
# on, and what is it waiting for.
|
|
#
|
|
# In JIT code the x64 backend keeps:
|
|
# rsi = PPCContext* rdi = guest membase
|
|
# (x64_emitter.cc: GetContextReg()=rsi, GetMembaseReg()=rdi)
|
|
# PPCContext offsets (computed against this build's header):
|
|
# r[0..31] @ +40 (8B each) ctr @ +296 lr @ +304
|
|
# thread_state @ +2704 virtual_membase @ +2712
|
|
#
|
|
# Needs ptrace: sudo sysctl -w kernel.yama.ptrace_scope=0 (restore with =1)
|
|
# Read-only: gdb detaches, the process keeps running.
|
|
#
|
|
# Usage: ./live-guest-state.sh ["Thread Name"] (default: Main XThread)
|
|
set -u
|
|
|
|
WANT="${1:-Main XThread}"
|
|
PID=$(pgrep -x xenia_canary | head -1)
|
|
[ -n "$PID" ] || { echo "no xenia_canary running"; exit 1; }
|
|
|
|
if [ "$(cat /proc/sys/kernel/yama/ptrace_scope 2>/dev/null)" != "0" ]; then
|
|
echo "ABORT: ptrace is locked (yama ptrace_scope != 0). Run once:"
|
|
echo " sudo sysctl -w kernel.yama.ptrace_scope=0"
|
|
exit 3
|
|
fi
|
|
|
|
RAW=$(mktemp /tmp/guest_state_XXXX.txt)
|
|
|
|
gdb -p "$PID" -batch \
|
|
-ex "set pagination off" -ex "set confirm off" \
|
|
-ex "thread find $WANT" \
|
|
-ex "thread apply all -ascending printf \"@@TH %d %s\\n\", \$_thread, \$_gthread" \
|
|
2>/dev/null | grep -E "Thread .* has name|@@TH" > "$RAW"
|
|
|
|
# gdb "thread find" prints e.g.: Thread 34 has target name 'Main XThread (F...'
|
|
GTH=$(grep -m1 "has .*name" "$RAW" | sed -E 's/.*Thread ([0-9]+) has.*/\1/')
|
|
[ -n "$GTH" ] || { echo "could not locate a thread named '$WANT'"; cat "$RAW"; exit 4; }
|
|
echo "gdb thread #$GTH == '$WANT' (pid $PID)"
|
|
|
|
gdb -p "$PID" -batch \
|
|
-ex "set pagination off" -ex "set confirm off" \
|
|
-ex "thread $GTH" \
|
|
-ex "echo \n=== host frame ===\n" \
|
|
-ex "printf \"host rip = %#lx\\n\", \$rip" \
|
|
-ex "bt 8" \
|
|
-ex "echo \n=== guest registers (PPCContext @ rsi) ===\n" \
|
|
-ex "set \$ctx = (unsigned long)\$rsi" \
|
|
-ex "printf \"ctx = %#lx\\n\", \$ctx" \
|
|
-ex "printf \"lr = %#lx\\n\", *(unsigned long*)(\$ctx+304)" \
|
|
-ex "printf \"ctr = %#lx\\n\", *(unsigned long*)(\$ctx+296)" \
|
|
-ex "printf \"r1(sp)= %#lx\\n\", *(unsigned long*)(\$ctx+40+8*1)" \
|
|
-ex "printf \"r3 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*3)" \
|
|
-ex "printf \"r4 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*4)" \
|
|
-ex "printf \"r5 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*5)" \
|
|
-ex "printf \"r6 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*6)" \
|
|
-ex "printf \"r7 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*7)" \
|
|
-ex "printf \"r8 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*8)" \
|
|
-ex "printf \"r9 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*9)" \
|
|
-ex "printf \"r10 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*10)" \
|
|
-ex "printf \"r11 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*11)" \
|
|
-ex "printf \"r12 = %#lx\\n\", *(unsigned long*)(\$ctx+40+8*12)" \
|
|
-ex "printf \"membase = %#lx\\n\", *(unsigned long*)(\$ctx+2712)" \
|
|
-ex "echo \n=== guest stack bytes @ r1 (BE; look for 82xxxxxx = code) ===\n" \
|
|
-ex "set \$mb = *(unsigned long*)(\$ctx+2712)" \
|
|
-ex "set \$sp = *(unsigned long*)(\$ctx+40+8*1)" \
|
|
-ex "x/128xb \$mb + \$sp" \
|
|
-ex "detach" 2>&1 | grep -vE "^\[|Reading symbols|no debugging symbols|Detaching"
|
|
|
|
rm -f "$RAW"
|
|
echo
|
|
echo "Guest code addresses look like 0x82xxxxxx — feed lr / stack hits to zq.py fn <pc>."
|