intro-video RE: deep demux-core instrumentation + handoff hygiene

Investigation probes for the ADV.wmv intro-video deadlock (feeder's demux
source-read 0x825211a0 returns no-data 0x80500000 where canary returns 0).

- xenia-kernel/state.rs: extend AUDIT-DEREF (fire_audit_pc_probe_if_match) to
  deep-dump the demux-state object (to +0xfc), its buffer descriptor [sub+4],
  the byte-source sub-objects ([sub+0]/[+0x30]/[+0x94] and [src0+0x2c]), and
  to WALK the windowed-buffer cached-block linked list (AUDIT-BLK: per-block
  64-bit start/size/dataptr) exactly as mapper 0x82522118 does. Read-only,
  env-gated (XENIA_AUDIT_DEREF); lockstep digest unaffected.
- xenia-app/main.rs: XENIA_DUMP_SLOTS observe-only scheduler runqueue dump.
- xenia-app/tests/sylpheed_oracles.rs: resolve ISO via SYLPHEED_ISO, then the
  repo-root sylpheed.iso symlink, then default; fix path typo.
- .gitignore: exclude .claude/ (71k files / 66GB agent worktrees) and the
  local investigation artifacts (audit-runs/, exit-thread-state.json, zq_*.py).

Findings (full chain in handoff notes): all ASF header metadata parses
correctly (packet size 16415, count 4728, 2 streams); cursors correct
(cur=5868, limit=77.6M); the windowed buffer works and slides (512B blocks);
no-data originates deep in the byte-read/parse chain, not from an empty buffer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-26 07:54:15 +02:00
parent 23189b95af
commit 7e9ee1ac33
4 changed files with 106 additions and 7 deletions

View File

@@ -1307,20 +1307,68 @@ impl KernelState {
if let Some((reg, deref_off)) = self.audit_deref {
use std::fmt::Write as _;
let base = ctx.gpr[reg as usize] as u32;
let dump64 = |label: &str, p: u32| {
let mut s = String::with_capacity(256);
let dumpn = |label: &str, p: u32, len: u32| {
let mut s = String::with_capacity(256 + (len as usize) * 4);
let _ = write!(&mut s, "AUDIT-DEREF {} ptr={:#010x}", label, p);
let mut o: u32 = 0;
while o < 64 {
while o < len {
let _ = write!(&mut s, " +0x{:02x}={:#010x}", o, mem.read_u32(p.wrapping_add(o)));
o += 4;
}
println!("{}", s);
};
println!("AUDIT-DEREF-HEAD pc={:#010x} tid={} cycle={} reg=r{} off=0x{:x}", pc, tid, cycle, reg, deref_off);
dump64("item", base);
dumpn("item", base, 64);
let sub = mem.read_u32(base.wrapping_add(deref_off));
dump64("sub", sub);
// STEP 42 — dump the demux-state object deep (to +0xfc): cursors
// [+0x10/0x18/0x20], state [+0x50], source handle [+0x94], count
// [+0x98], gate flags [+0xb8/bc/cc/d4]. Then follow the buffer
// descriptor [sub+4] (bounds at +0x10/+0x14) which defines the
// window of file bytes available to the demux at the cursor.
dumpn("sub", sub, 0x100);
let bufdesc = mem.read_u32(sub.wrapping_add(4));
dumpn("bufdesc", bufdesc, 0x40);
// STEP 43 — follow the byte-source sub-objects that actually hold
// the windowed file bytes + read position: [sub+0] (byte-source /
// embedded vtable obj 0x40553640), [sub+0x30] (0x40688560),
// [sub+0x94] (source handle 0x41073e40). One holds the data ptr +
// cursor; an empty/short buffer here is the no-data root.
let src0 = mem.read_u32(sub);
dumpn("src0", src0, 0x80);
dumpn("src30", mem.read_u32(sub.wrapping_add(0x30)), 0x80);
dumpn("src94", mem.read_u32(sub.wrapping_add(0x94)), 0x80);
// STEP 44 — the byte-source's buffer/cursor holder [src0+0x2c]
// (=0x40930f60): vtable[4]=0x82524080 reads its file-offset cursor
// [+0x28], cached-range [+0x80]/[+0x88], flags [+0x40], and maps
// offsets to memory blocks via 0x82522118 (the windowed/chunk
// buffer). An empty cached range / null block ptr here = the data
// byte-source never fed → 0x80500000. Follow its block ptr [+0x18].
let cur = mem.read_u32(src0.wrapping_add(0x2c));
dumpn("src0_2c", cur, 0x100);
// STEP 45 — walk the cached-block linked list exactly as the
// mapper 0x82522118 does: container=[cur+0x10]; node=[container+8];
// each node: block=[node+0] (block[+8]=start off64, block[+4]=size),
// next=[node+8]. Print each block's 64-bit start + size so we can
// see whether ANY cached block covers file offset 5868 (the first
// ASF data packet). Empty list / no covering block = the root.
let container = mem.read_u32(cur.wrapping_add(0x10));
dumpn("blkcont", container, 0x20);
let mut node = mem.read_u32(container.wrapping_add(8));
let mut k = 0;
while node != 0 && k < 8 {
let block = mem.read_u32(node);
let start_hi = mem.read_u32(block.wrapping_add(8));
let start_lo = mem.read_u32(block.wrapping_add(0xc));
let size = mem.read_u32(block.wrapping_add(4));
let dptr = mem.read_u32(block);
println!(
"AUDIT-BLK k={} node={:#010x} block={:#010x} start={:#010x}_{:#010x} size={:#010x} dataptr={:#010x}",
k, node, block, start_hi, start_lo, size, dptr,
);
node = mem.read_u32(node.wrapping_add(8));
k += 1;
}
println!("AUDIT-BLK-END count={}", k);
let vt = mem.read_u32(sub); // [sub+0] = vtable
// Dump 48 vtable slots so slot 28 (+0x70) and slot 36 (+0x90) show.
let mut s = String::with_capacity(512);