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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user