This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/tools/re-capture/freeze_waitobj.sh
Sylpheed RE agent bfd4d55ce9 re: two wait object types, and the read validates itself
Re-extracting the same gdb capture per thread rather than by grep qualifies the
previous entry. Of eighteen threads whose frame 3 is XObject::Wait, eight have
[rbx] equal to the XEvent vtable plus sixteen, two equal to the XSemaphore
vtable plus sixteen, and eight hold a pointer into the mmap region that is not a
vtable at all. So the waits are on two distinct kernel types, XEvent and
XSemaphore, and the earlier claim that the object is an XEvent was right for the
majority but not the whole picture.

The eight non-vtable readings are the method checking itself rather than a
failure. A polymorphic object's first word is always a vtable pointer, so those
values are simply not this: rbx could not be restored for those frames and the
unwind returned whatever the register held. A reading counts only if [rbx] lands
in the binary's vtable range and resolves to a "vtable for" symbol; anything
else is discarded rather than interpreted. Ten of eighteen resolve and the rest
are honestly unknown.

That also settles the previous entry's worry that the 0x7ffc and 0x7ffd
addresses looked like stack. They are the shared mmap region, which holds thread
stacks and large allocations alike, so the vtable check rather than the address
range is what separates an object from a stack slot.

The follow-up run that would have added /proc/<pid>/maps classification and a
wider object dump never booted -- EMULATOR GONE at 0s, skip_intro exit 4 --
most likely a stale emulator or lockfile from the preceding gdb session, whose
process tree is parented differently and escaped the usual cleanup. So the map
classification, the multi-word object dump and the frozen-state capture are all
still unrun.
2026-08-25 08:10:56 +00:00

70 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Read WHICH object the frozen guest threads are waiting on.
#
# Groundwork (mission-freeze-resume-spin.md): XObject::Wait keeps `this` in %rbx,
# and .eh_frame lets gdb restore callee-saved registers during the unwind, so no
# DWARF and no rebuild are needed -- select the Wait frame, read rbx, then read
# the vtable pointer at [rbx] and resolve it in the symtab.
set -u
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages
export XENIA_BIN=/sylph-home/re/bin/gdb-wrap/xenia_canary
SD="$(cd "$(dirname "$0")" && pwd)"
FLY="${1:-240}"
CMD=/tmp/gdb-cmd; OUT=/tmp/gdb-out.log
"$SD/launch_mission.sh" fly || { echo "BOOT FAILED"; exit 1; }
CFG=/tmp/nav-fz.json
for t in 1 2 3; do
python3 "$SD/pad.py" set "rt=1" >/dev/null 2>&1 || true; sleep 3
python3 "$SD/pad.py" clear >/dev/null 2>&1 || true
if python3 "$SD/entities2.py" self 0x130 "$CFG" >/dev/null 2>&1; then
SYLPH_HUNT=1 SYLPH_KEEPOUT=1400 nohup python3 "$SD/pilot.py" "$CFG" "$FLY" \
</dev/null >/tmp/fz-pilot.log 2>&1 & P=$!; echo "--- pilot flying"; break
fi
done
echo "--- flying ${FLY}s (boot under gdb costs ~300s, so this must leave room"
echo "--- for the gdb step inside one call: timeout kills the whole group)"
python3 -c "import time,sys; time.sleep(int(sys.argv[1]))" "$FLY"
[ -n "${P:-}" ] && kill "$P" 2>/dev/null
pid=$(pgrep -x gdb | head -1)
[ -n "$pid" ] || { echo "no gdb"; exit 2; }
before=$(wc -c < "$OUT")
kill -INT "$pid"; sleep 3
{
echo 'echo === HOT THREAD WAIT OBJECTS ===\n'
echo 'thread apply all bt 6'
echo 'echo === END BT ===\n'
} >> "$CMD"
sleep 10
tail -c +$((before + 1)) "$OUT" > /tmp/fz-bt.txt
# find threads whose frame 3 is XObject::Wait, then read rbx there
python3 - <<'PY' >> "$CMD"
import re
txt=open('/tmp/fz-bt.txt').read()
cur=None
for line in txt.splitlines():
m=re.match(r'Thread (\d+) ', line)
if m: cur=m.group(1)
if cur and 'XObject::Wait' in line:
print(r'echo === T%s ===\n' % cur)
print('thread %s' % cur)
print('frame 3')
print('info registers rbx')
print('x/8gx $rbx') # vtable, KernelState*, fields -- or saved regs
cur=None
print(r'echo === END OBJ ===\n')
print('continue')
PY
sleep 12
# Is $rbx in a STACK mapping or an anonymous/heap one? 0x7ffc.. is the shared
# mmap region on x86-64: thread stacks live there, but so do large allocations,
# so the address range alone cannot tell them apart. /proc/<pid>/maps can.
epid=$(pgrep -x xenia_canary | head -1)
[ -n "$epid" ] && cp "/proc/$epid/maps" /tmp/fz-maps.txt 2>/dev/null && \
echo "--- saved /proc/$epid/maps ($(wc -l < /tmp/fz-maps.txt) mappings)"
screenshot /tmp/fz-screen.png >/dev/null 2>&1
echo "--- screen at capture time:"
python3 "$SD/screen_id.py" /tmp/fz-screen.png 2>/dev/null | head -2
tail -c +$((before + 1)) "$OUT" | sed -n '/HOT THREAD WAIT OBJECTS/,$p' | grep -vE "^\s*$" | tail -70
echo "FREEZE WAITOBJ DONE"