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>
103 lines
5.5 KiB
Markdown
103 lines
5.5 KiB
Markdown
# Phase C+15-α Schema-Wiring Audit (2026-05-14)
|
||
|
||
## Phase 1 — Wired/unwired matrix (pre-session)
|
||
|
||
| Kind | Canary emits? | Ours emits? | Status (pre) | Priority |
|
||
|---------------------|---------------|-------------|---------------|----------|
|
||
| `schema_version` | yes | yes | wired | — |
|
||
| `import.call` | yes | yes | wired | — |
|
||
| `kernel.call` | yes | yes | wired (+C+10) | — |
|
||
| `kernel.return` | yes | yes | wired | — |
|
||
| `handle.create` | declared | declared | **stubbed** | HIGH |
|
||
| `handle.destroy` | declared | declared | **stubbed** | HIGH |
|
||
| `thread.create` | declared | declared | **stubbed** | HIGH |
|
||
| `thread.exit` | declared | declared | **stubbed** | HIGH |
|
||
| `wait.begin` | declared | declared | **stubbed** | HIGH |
|
||
| `wait.end` | declared | declared | **stubbed** | HIGH |
|
||
| `thread.suspend` | declared | not in API | unwired | LOW |
|
||
| `thread.resume` | declared | not in API | unwired | LOW |
|
||
| `vfs.open` | declared | not in API | redundant? | MEDIUM |
|
||
| `vfs.read` | declared | not in API | high-vol | LOW |
|
||
| `vfs.close` | declared | not in API | redundant? | MEDIUM |
|
||
| `mem.write` | declared | not in API | opt-in | LOW |
|
||
|
||
## Phase 2/3 — Kinds wired this session
|
||
|
||
Wired symmetrically in both engines (cvar-gated default-off):
|
||
|
||
- **`handle.create`** — emitted from `KernelState::alloc_handle_for` (ours) /
|
||
`ObjectTable::AddHandle` (canary). 39+ call sites covered via centralized hook.
|
||
- **`handle.destroy`** — emitted from `nt_close` + `xam_task_close_handle` (ours) /
|
||
`ObjectTable::RemoveHandle` (canary).
|
||
- **`thread.create`** — emitted from `ex_create_thread` (ours) / `ExCreateThread`
|
||
in `xboxkrnl_threading.cc` (canary). After spawn succeeds.
|
||
- **`thread.exit`** — emitted from `ex_terminate_thread` (ours) / `XThread::Exit`
|
||
(canary). Canary's `XThread::Exit` covers both explicit `ExTerminateThread`
|
||
and implicit thread-entry returns.
|
||
- **`wait.begin`** — emitted from `nt_wait_for_single_object_ex` +
|
||
`ke_wait_for_single_object` (ours) / `xeKeWaitForSingleObject` +
|
||
`NtWaitForSingleObjectEx` (canary).
|
||
|
||
Deferred (v1.2):
|
||
|
||
- **`wait.end`** — design challenge: wait can park the guest thread, and the
|
||
wake-status path differs between engines. Sync outcome status is already
|
||
captured in the immediately-following `kernel.return`. Async wake outcome
|
||
surfaced in subsequent events.
|
||
- **`thread.suspend` / `thread.resume`** — low-frequency; defer until needed.
|
||
- **`vfs.*`** — redundant with `kernel.call` for Nt*File. Skip per schema-v1
|
||
audit recommendation.
|
||
- **`mem.write`** — opt-in only (separate cvar); high-volume.
|
||
|
||
## Code summary
|
||
|
||
### Ours (~140 LOC)
|
||
|
||
- `crates/xenia-kernel/src/event_log.rs` — registry + auto helpers
|
||
(`register_handle_semantic_id`, `lookup_handle_semantic_id`,
|
||
`forget_handle_semantic_id`, `emit_handle_create_auto`,
|
||
`emit_handle_destroy_auto`). +85 LOC.
|
||
- `crates/xenia-kernel/src/objects.rs` — `KernelObject::schema_object_type()`.
|
||
+14 LOC.
|
||
- `crates/xenia-kernel/src/state.rs` — `alloc_handle_for` emit hook. +24 LOC.
|
||
- `crates/xenia-kernel/src/exports.rs` — `nt_close` destroy emit,
|
||
`ex_create_thread` thread.create emit, `ex_terminate_thread` thread.exit emit,
|
||
`nt_wait_for_single_object_ex` + `ke_wait_for_single_object` wait.begin emits,
|
||
+ `decode_timeout_ns` helper. +85 LOC.
|
||
- `crates/xenia-kernel/src/xam.rs` — `xam_task_close_handle` destroy emit. +14 LOC.
|
||
|
||
### Canary (~130 LOC)
|
||
|
||
- `src/xenia/kernel/event_log.h` — registry API (`RegisterHandleSemanticId`,
|
||
`LookupHandleSemanticId`, `ForgetHandleSemanticId`, `EmitHandleCreateAuto`,
|
||
`EmitHandleDestroyAuto`). +20 LOC.
|
||
- `src/xenia/kernel/event_log.cc` — per-tid counter map (was per-host-thread
|
||
`thread_local`; produced duplicate `tid_event_idx` for tid=0 across host
|
||
threads — a bug in the pre-session implementation), `CurrentTid` non-asserting
|
||
via new `XThread::TryGetCurrentThread`, registry helpers, auto-emit wrappers.
|
||
+60 LOC net.
|
||
- `src/xenia/kernel/xthread.h` + `xthread.cc` — `TryGetCurrentThread` accessor
|
||
+ `XThread::Exit` thread.exit emit. +12 LOC.
|
||
- `src/xenia/kernel/util/object_table.cc` — `AddHandle`/`RemoveHandle` hooks
|
||
+ `SchemaObjectType` mapping. +35 LOC.
|
||
- `src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc` — `ExCreateThread`
|
||
thread.create emit, `xeKeWaitForSingleObject` + `NtWaitForSingleObjectEx`
|
||
wait.begin emits. +30 LOC.
|
||
|
||
### Diff tool
|
||
|
||
- `tools/diff-events/diff_events.py` — `SKIP_PAYLOAD_FIELDS_BY_KIND` now skips
|
||
`handle_semantic_id` (cross-engine `creating_tid` differs, so SIDs are
|
||
engine-local), `parent_tid`, `handles_semantic_ids`, `woken_by_semantic_id`.
|
||
+6 LOC.
|
||
|
||
## Bug found and fixed this session
|
||
|
||
**Pre-session bug**: canary's `t_tid_event_idx` was a host-thread-local global,
|
||
not a tid-keyed counter. When `AddHandle` runs from multiple host threads with
|
||
tid==0 (boot init + early XThread bootstrap before guest tid is assigned), each
|
||
host thread had its own counter starting at 0, producing duplicate
|
||
`tid_event_idx` values within the tid=0 stream. The diff tool rejected the
|
||
file with "events out of order at index 8". Fixed by replacing the thread_local
|
||
with a tid-keyed `std::unordered_map` + mutex (matches ours's design).
|