ptrace breakpoints in a Wine game leave every thread stopped
Symptoms
A Python recorder attached to every thread of the Wine process with PTRACE_SEIZE, wrote int3 at a few addresses in the game's step, and stepped over each hit. It recorded two or three hits, then every thread of the game sat in a tracing stop ("t" in /proc/PID/task/*/stat), while the recorder waited in wait4 for a stop that never came. After the recorder was killed (with the original bytes written back first through /proc/PID/mem), the game crashed.
Cause
Not pinned down. Wine uses signals between its threads (suspension, exceptions). A tracer has to deliver every one of them in the right order, with group stops and interrupt stops handled, or the process hangs.
What to do
Record from inside the game instead: a small code cave patched into a copy of the EXE writes the state at each point of interest to a file (run/vm/steplog.s, run/vm/patch_steplog.py). The game runs at full speed, and each record is exact for its instant. Keep the cave inert until a word in memory turns it on (run/vm/steplog.py). Then the patched copy can stay installed.
Room for the cave: the end of the code section's file bytes, and the slack after the relocations in .reloc. Make .reloc executable in the copy's section header (the EXE is never relocated). Put the cave's data in the slack of a writable page (.idata past its file bytes). Call imports through the IAT (CreateFileA, WriteFile).