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>
62 lines
4.7 KiB
Markdown
62 lines
4.7 KiB
Markdown
# Phase B — ours changes inventory
|
||
|
||
All instrumentation is additive and cvar-gated default-off. With
|
||
`KernelState::phase_b_snapshot_dir == None` (the default), the
|
||
worker_prologue hook is one Option-tag test; the emitter module is
|
||
unreached. Gate 1 verified `xenia-rs check --stable-digest -n 50M`
|
||
produces a byte-identical digest pre/post-patch (see `validation.md`).
|
||
|
||
## New files
|
||
|
||
- [crates/xenia-kernel/src/phase_b_snapshot.rs](../../../crates/xenia-kernel/src/phase_b_snapshot.rs) — emitter for all five snapshot files + manifest. Stdlib `serde_json` + `sha2`; no new transitive deps beyond what was already pulled into the workspace. ~590 LOC.
|
||
- [tools/diff-state/diff_state.py](../../../tools/diff-state/diff_state.py) — stdlib-only Python. Reads both engines' snapshot dirs, classifies divergences by class (σ-structural, δ-content, γ-kernel-content, κ-cache, ε-host-allocator, τ-host-timing), enforces STOP gate on `image_loaded_sha256`/`xex_entry_point`/`iso_sha256`. ~380 LOC.
|
||
- [tools/diff-state/README.md](../../../tools/diff-state/README.md) — usage + rules reference.
|
||
|
||
## Modified files
|
||
|
||
### [crates/xenia-app/src/main.rs](../../../crates/xenia-app/src/main.rs)
|
||
|
||
- Around line 251: three new `Exec` flags — `--phase-b-snapshot-dir <DIR>`, `--phase-b-snapshot-and-exit`, `--phase-b-dump-section-content`.
|
||
- Around line 420: env-var fallback (`XENIA_PHASE_B_SNAPSHOT_DIR`, `XENIA_PHASE_B_SNAPSHOT_AND_EXIT`, `XENIA_PHASE_B_DUMP_SECTION_CONTENT`); plumbed through `cmd_exec` (signature gained three trailing args).
|
||
- Around line 945: `kernel.entry_pc = entry; kernel.phase_b_snapshot_dir = …;` etc. — feeds the resolved values to `KernelState` for the hook to read.
|
||
- Around line 2228 (`worker_prologue`): single hook call into `xenia_kernel::phase_b_snapshot::fire_if_entry_thread(kernel, mem, pc, current_tid)`, gated by `kernel.phase_b_snapshot_dir.is_some()` (zero-cost when None).
|
||
- `cmd_check` updated to thread three `None`/`false` defaults so the golden digest path stays unaffected.
|
||
|
||
### [crates/xenia-kernel/src/state.rs](../../../crates/xenia-kernel/src/state.rs)
|
||
|
||
- Four new public fields on `KernelState`: `phase_b_snapshot_dir: Option<PathBuf>`, `phase_b_snapshot_and_exit: bool`, `phase_b_dump_section_content: bool`, `entry_pc: u32`. Default-constructed `None`/`false`/`0`.
|
||
|
||
### [crates/xenia-kernel/src/lib.rs](../../../crates/xenia-kernel/src/lib.rs)
|
||
|
||
- Adds `pub mod phase_b_snapshot;` to the module list (alphabetical position after `objects`).
|
||
|
||
### [crates/xenia-kernel/Cargo.toml](../../../crates/xenia-kernel/Cargo.toml)
|
||
|
||
- New dependencies: `serde_json` (workspace), `sha2` (workspace, newly added), `libc = "0.2"` (for `_exit`).
|
||
|
||
### [Cargo.toml](../../../Cargo.toml) (workspace)
|
||
|
||
- New `sha2 = "0.10"` workspace dependency.
|
||
|
||
## Snapshot mechanism
|
||
|
||
When `phase_b_snapshot_dir` is `Some`, the hook in `worker_prologue` calls
|
||
`fire_if_entry_thread` exactly once. The helper:
|
||
|
||
1. Fast-path early-returns when `phase_b_snapshot_dir == None` (Option-tag check).
|
||
2. Returns if `DONE` is already set (subsequent slot visits).
|
||
3. Returns if `pc != entry_pc || current_tid != INITIAL_GUEST_TID` (this slot visit is not the entry thread's first instruction).
|
||
4. CAS-claims `CLAIMED` (one-shot guard against any race).
|
||
5. Calls `write_snapshot`, which builds five `serde_json::Value` trees, serializes each with `serialize_sorted` (a deterministic walker that sorts object keys via `BTreeMap`-equivalent), writes each via `File::create + flush + sync_all`, indexes the SHA-256s into `manifest.json`.
|
||
6. If `phase_b_snapshot_and_exit`, calls `libc::_exit(0)` so the snapshot is durable and the process terminates before the host scheduler or other threads can perturb on-disk state.
|
||
|
||
## What's in each snapshot file (ours side)
|
||
|
||
| file | content |
|
||
|---|---|
|
||
| `cpu_state.json` | `pc` (= entry_pc), `gpr[32]` (raw u64 hex), `fpr[32]` (raw bit-pattern hex), `vr[128]` + `vscr` (32-hex BE byte order), `cr[8]`, `xer`/`msr`/`ctr`/`lr`/`vrsave`/`fpscr`, `thread_id`, `stack_base/limit`, `tls_base`, `pcr_base`. |
|
||
| `memory.json` | `regions[]` — named ranges (XEX image, main stack, PCR, TLS) each with SHA-256. `heaps[]` — 4 heap descriptors with committed-page histograms. `committed_pages_total`. |
|
||
| `kernel.json` | `objects[]` (sorted by FNV-1a stable `handle_semantic_id`) — type, type_code, details (per-type fields like `thread_id`/`is_entry_thread`). `exports_registered_count/sha256/sample[]`. |
|
||
| `vfs.json` | `resolve_path_probes[]` — canonical 10-path probe set. `mounted_devices_observed_count`. `cache_root_listing[]`. |
|
||
| `config.json` | `xex_entry_point`, `xex_image_base/size`, `image_loaded_sha256` (the primary cross-engine invariant), `cvars{}`, `host_ns_at_snapshot` / `wall_clock_iso8601` (deterministic_skip-flagged). |
|