Files
Sylpheed/tools/live-guest-state.sh
sim e45a56ad83 chore: hand the workspace over — the gated launchers, and the state of play
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>
2026-09-18 07:17:36 +02:00

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>."