handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes

Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-05 07:19:08 +02:00
parent acd1656753
commit ef93a4fa14
620 changed files with 108303 additions and 1 deletions

View File

@@ -0,0 +1,81 @@
# AUDIT-068 Session 1 — host-side memory-write watch (canary instrumentation)
Date: 2026-05-19
## Goal
Capture which host C++ functions perform the writes to guest memory that ours never reproduces:
1. Vtable install at `0xBCE25340 = 0x8200A208` (and clone `0x8200A928`) — gates `sub_825070F0`.
2. Voice-struct field clear `[VOICE+0x164]` (value `0x00000000`, on guest-VA likely in heap `0x425xxxxx`).
3. Anything else surfaced.
## Write-path surface inventory (canary)
### A. `xe::store_and_swap<T>` template family (`xenia-canary/src/xenia/base/memory.h:410-475`)
- Sized specializations for T = int8/uint8/int16/uint16/int32/uint32/int64/uint64/float/double.
- String specializations recurse to `store_and_swap<uint8_t>` / `<uint16_t>`.
- Receives `void* mem` = HOST pointer; does `*p = byte_swap(value)`.
- **This is the canonical path** for host-side typed writes to guest memory used by kernel-import handlers in `xboxkrnl_*.cc`. Confirmed wide use (16 kernel sub-modules call `store_and_swap<uint32_t>` alone).
- Vtable install (PPC `stw vptr,0(obj)` equivalent on host side) almost certainly uses `store_and_swap<uint32_t>(host_ptr, vptr)`.
### B. `Memory::Zero/Fill/Copy` (`xenia/memory.cc:542-554`)
- Use `std::memset`/`std::memcpy` directly via host pointer (after `TranslateVirtual`).
- Wrappers for `RtlZeroMemory`, `RtlFillMemory`, `RtlMoveMemory`, `RtlCopyMemory`.
- Bypass `store_and_swap` — must instrument separately if we want full coverage.
- Voice-struct clears via `0x00000000` could plausibly come through here (RtlZeroMemory) or directly via `store_and_swap<uint32_t>` (typed write).
### C. Direct guest writes via `*TranslateVirtual<T*>() = …`
- Some sites cast and write through the host pointer directly without going through `store_and_swap`.
- Lower coverage priority — start with A+B; add C only if first 2 don't catch our targets.
## Cvar design (mimics audit_67 pattern)
Two new cvars in `xenia/cpu/cpu_flags.{h,cc}`:
```cpp
DECLARE_string(audit_68_host_mem_watch_values); // CSV of u32 values (max 8)
DECLARE_string(audit_68_host_mem_watch_addrs); // CSV of guest VAs or VA ranges (max 8)
```
Format examples:
- Values: `--audit_68_host_mem_watch_values=0x8200A208,0x8200A928`
- Addrs single: `--audit_68_host_mem_watch_addrs=0xBCE25340`
- Addrs range: `--audit_68_host_mem_watch_addrs=0x42500000-0x42600000,0xBCE25340`
Default empty → zero overhead.
Sample log line (XELOGI):
```
AUDIT-068-HOST-WRITE guest_va=0xBCE25340 val=0x8200A208 sz=4 fn=<host_function> host_ns=10123456789 tid=N
```
`fn=<host_function>` is filled by the caller (each `store_and_swap<T>` specialization passes `__FUNCTION__` or a tag). We can't get a real backtrace cheaply across MSVC; we instead instrument the high-fanout entry points (kernel-import handlers, `Memory::Zero/Fill/Copy`) with a string tag. For Session 1, capture is sufficient with just template name + caller tag.
## Implementation strategy
1. New file `xenia/audit_68_host_mem_watch.h` (top-level): forward decls of helper functions in `namespace xe::audit_68`:
```cpp
extern std::atomic<uint8_t> g_active; // 0=off, 1=values, 2=addrs, 3=both
void check_host_write(const void* host_ptr, uint64_t value, uint8_t size,
const char* tag);
void check_guest_write(uint32_t guest_va, uint64_t value, uint8_t size,
const char* tag);
```
2. New file `xenia/audit_68_host_mem_watch.cc`: lazy-parse the cvars on first call, atomic-bool sets active. Performs `Memory::active()->HostToGuestVirtual(host_ptr)` translation, then matches against value-list and addr-range list, emits XELOGI.
3. `xenia/memory.h`: add public static `Memory::active()` (returns `active_memory_`).
4. `xenia/base/memory.h`: extend `store_and_swap<T>` specializations (uint8/uint16/uint32/uint64 only — the integer typed paths most likely to write vptrs / clear flags) to check `g_active` and call the helper. Hot path: 1 atomic load + branch when off. The added cost when on is one cmp+jne per byte/word/dword/qword store; acceptable for capture runs.
5. `xenia/memory.cc`: instrument `Memory::Zero/Fill/Copy` with calls to `check_guest_write` (each ranges through the affected guest VAs; for capture purposes we only log the first matching byte+size+tag — we don't expand to per-byte events).
Total estimated LOC: ~120-160 LOC across 5 files.
## Capture protocol
- Build canary with the new code.
- Smoke test: `--audit_68_host_mem_watch_values=0x12345678` (no expected hits) → confirm no spurious lines, build/init OK.
- Sanity test: `--audit_68_host_mem_watch_values=0x82000000` (very common vtable-base) → confirm many lines, then revert.
- Run 1 (vtable install): `--audit_68_host_mem_watch_values=0x8200A208,0x8200A928 --mute=true`. Kill after ~90s.
- Run 2 (voice-struct clear): `--audit_68_host_mem_watch_addrs=0x42500000-0x42600000 --mute=true`. Kill after ~30s (this is heap-region wide; likely lots of hits, capture early window only). May need narrower range once we see the first writes.
- Cold-protocol: backup canary's cache (`xenia-canary/build-cross/bin/Windows/Debug/cache/`) to `/tmp/canary-cache-bak-audit-068`, wipe before run, restore after.