Files
xenia-rs/docs/functions/sub_821B55D8.md
MechaCat02 ad45873a1b ITERATE-2.V: scheduler priority aging closes 18-day AUDIT-049 wedge
Priority aging in xenia-cpu/scheduler.rs:pick_runnable
(effective_priority = base + age_bonus(now_round - last_run_round),
capped at +31, AGING_ROUNDS_PER_BONUS=1). Strict-priority was parking
priority=0 threads behind CPU-bound priority=15 audio mixer
(sub_824D1328 guest spinwait at PC=0x824d1404 on CPU5). Aging
eventually picks the starved thread, breaking the producer-consumer
cycle that caused 5-tid wedge at PC=0x824ac578 since AUDIT-049 (10 May).

Cascade observed: tid=13 clean exit; events 121K -> 13M (107x); last
host_ns 767ms -> 51,011ms (66x); 8 new threads spawn; VdSwap 1 -> 2.

Complete two-day iterate sequence (2026-05-27 -> 2026-05-28):
- 2.F: VdSwap drain timeout 900ms -> 1ms (xenia-gpu/handle.rs); 876x
       perf win on VdSwap kernel callback
- 2.H: vA0000000 physical heap bucket added (state.rs, exports.rs);
       ctx_ptrs now in 0xA0000000-0xBFFFFFFF range matching canary
- 2.L: Phase-A diff harness categorized [return_value mismatch],
       [status mismatch], [args_resolved.path mismatch] tags
       (tools/diff-events/diff_events.py); closes reading-error #41
       (silent test-harness state leak invalidating trace diffs)
- 2.M: always-on exit-thread-state.json sibling to Phase-A JSONL
       (event_log.rs + xenia-app/main.rs); closes reading-error #42
       (Phase-A blind to blocked-forever waits)
- 2.Q: signal.match kernel instrumentation in NtSetEvent /
       NtReleaseSemaphore / KeSetEvent / KeReleaseSemaphore
       (exports.rs); emits target_handle + waiter_count + waiter_tids
- 2.T: wake.requested kernel instrumentation in wake_eligible_waiters
       (exports.rs); emits target_tid + transition + new_state
- 2.V: scheduler priority aging (xenia-cpu/scheduler.rs) [keystone]

Plus accumulated WIP from earlier May (contention_manifest,
phase_b_snapshot, xam/xaudio enhancements, analysis db, xex loader,
xenia-app main loop, etc.). Audit-runs/ artifacts remain untracked
per project convention.

Tests: 300 xenia-cpu / 227 xenia-kernel / 5 xenia-app / 19 xenia-path
/ 30+ smaller suites -- all PASS, 0 regressions. Determinism preserved
(2x cold runs bit-identical at 13,003,881 events post-2.V).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 07:27:26 +02:00

54 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
address: 0x821B55D8
classification: normal_callee
confidence: high
last_audit: 064
aliases:
- "AUDIT-058 caller-ladder fn #5 (vtable slot 6 of class containing 0x82172D88 dispatcher)"
---
# sub_821B55D8 — vtable slot 6 invoked from sub_82172BA0 dispatcher
## Synopsis
Normal callee dispatched via the `bctrl` at `sub_82172BA0+0x1E8` (PC `0x82172D88`) — slot 6 of some game-object vtable (offset 24 = `lwz r11, 24(r11)`). Calls [sub_824F8398](sub_824F8398.md) at PC `0x821B5B5C` (=+0x584). Note the **only static caller is via `b` (jump, NOT bl)** from `sub_821B6DF4+0x40` — that's the MSVC EH catch-handler trampoline at PC `0x821B6E34`. **AUDIT-064 falsifies the AUDIT-058 framing that this is reached primarily via the EH path**: at runtime it's reached via the `bctrl` slot-6 dispatch from `sub_82172BA0`, not via the EH thunk.
## Evidence
- Disasm prolog at `0x821B55D8`: `mflr r12; bl 0x825F0F74; stfd f31, -88(r1); subi r31, r1, 368; stwu r1, -368(r1); mr r30, r3; ...` — standard normal-callee prolog. Uses `subi r31, r1, 368` (frame-pointer is `r1-368`), NOT MSVC EH-handler's `subi r31, r12, N`.
- Function size: 2076 bytes / 519 insns. `has_eh=True`, `frame_size=0` per DB (but the actual stack alloc is 368 bytes — `frame_size=0` likely indicates dynamic).
- Static caller xref (sole): PC `0x821B6E34` inside `sub_821B6DF4` via `kind=j insn=b` (unconditional branch, NOT bl). This is an EH catch-handler trampoline that tail-jumps into this fn's body — it's how the MSVC EH machinery enters the fn AFTER a matching exception is caught. Pattern at `0x821B6E30..0x821B6E34`: `lwz r3, 8(r3); b 0x821B55D8`.
- AUDIT-064 canary 60s probe: fires 1× with `lr=0x82172D8C r3=BCCC52C0 r4=FFFFFFFF r5=01000000 r6=00000000` on tid=6. `lr=0x82172D8C` is the post-bctrl PC inside `sub_82172BA0+0x1E8`. Reproduced at 120s and 180s.
- AUDIT-064 ours `--ctor-probe=0x821B55D8` -n 500M: **0 fires**.
## Activation
**Primary (runtime)**: vtable slot 6 dispatch from `sub_82172BA0+0x1E8 bctrl` (PC `0x82172D88`). The dispatcher walks an array of objects (loaded from `[r29+56]`) and invokes vtable slot 6 on each. Slot 6 = `lwz r11, 24(r11)` where r11 is the vtable.
**Secondary (EH path)**: MSVC catch-handler at `sub_821B6DF4+0x40` tail-jumps here when a matching exception is caught. Not the runtime activation path observed in either engine at this horizon.
## Static graph
- Static callers (DB):
- `sub_821B6DF4+0x40` via `b 0x821B55D8` (EH thunk, NOT a `bl` — reached via exception dispatch only).
- No `bl` static callers recorded — but **AUDIT-064 captured `lr=0x82172D8C` at runtime fire**, meaning the actual `bl`-equivalent caller is the bctrl at `sub_82172BA0+0x1E8`. The static analyzer's ind_call list for PC `0x82172D88` includes many observed targets but NOT this fn (gap in the dynamic-target inference).
- Callees: `sub_824F8398` at PC `0x821B5B5C`, plus many others (`sub_821707C0`, `sub_822F13B0`, `sub_822F2A00`, `sub_823C2990`, ...).
## Audit log
- **AUDIT-064 (2026-05-12)** — disasm confirms normal-callee prolog (refutes "EH handler" hypothesis). Canary fires 1× / ours 0×. **Real runtime caller is `sub_82172BA0+0x1E8 bctrl`, NOT `sub_821B6DF4` EH thunk.** The DB xref via `b` from EH is a secondary entry path. **New reading-error class observed**: static xrefs for `bctrl` indirect targets are populated by some dynamic-target inference but it has gaps — must cross-check at runtime via `--audit_61_branch_probe_pcs` + LR resolution. [confirmed]
- **AUDIT-058 (2026-05-10)** — flagged as part of static caller ladder under `sub_821B6DF4`. [STATUS: partially falsified by AUDIT-064 — the runtime path is the bctrl from sub_82172BA0, not the EH thunk.]
## Open questions
- Which class's vtable has slot 6 = `sub_821B55D8`? The instance loaded by `sub_82172BA0` at `[r3+24]` from the array. Possibly `silph::GamePart_Title` or a sibling — would need to enumerate `sub_82172BA0`'s array-walk target instances at runtime.
- Why does the DB's `xrefs` (kind=`ind_call`) for source `0x82172D88` not list `sub_821B55D8` as a target? The dynamic-target inference appears to populate from a separate trace, missing this one.
## Cross-references
- Callees: `sub_824F8398` (PC `0x821B5B5C`).
- EH-secondary entry: `sub_821B6DF4+0x40` (`b 0x821B55D8`).
- Runtime caller (bctrl): `sub_82172BA0+0x1E8` (PC `0x82172D88`).
- Audits: 058, 060, 064.
- Artifacts: `audit-runs/audit-064-activation-ladder/canary-{60,120,180}s.log`, `canary-upstream-60s.log`.