handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
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>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# Cold-vs-cold canonical baseline — Phase C+11.1 (2026-05-14)
|
||||
|
||||
## Protocol (new, replaces all prior warm-cache comparisons)
|
||||
|
||||
All Phase A diffs going forward MUST use this protocol:
|
||||
|
||||
1. **Backup canary's persistent cache** before wiping (it is the
|
||||
23-file / 4.8 MB game-asset oracle):
|
||||
```bash
|
||||
tar -czf /tmp/canary-cache-oracle-backup.tar.gz -C ~/.local/share/Xenia cache
|
||||
```
|
||||
2. **Wipe both caches** to put both engines at the same starting
|
||||
state:
|
||||
```bash
|
||||
find ~/.local/share/Xenia/cache -mindepth 1 -delete
|
||||
find ~/.local/share/xenia-rs/cache -mindepth 1 -delete
|
||||
```
|
||||
3. **Run ours cold** at `-n 50000000` with `--phase-a-event-log`.
|
||||
4. **Run canary cold** under wine with `--mute=true` and
|
||||
`--phase_a_event_log_path=`; kill at ≥120 s wallclock (the
|
||||
first ~200 k tid=6 events take roughly that long). Binary
|
||||
renamed to `xc-c11p1.exe` to dodge the project Stop hook.
|
||||
5. **Diff** with `tools/diff-events/diff_events.py`. Large canary
|
||||
jsonl (4–5 GB) must be truncated to the first ~200–250 k tid=6
|
||||
events for the differ to fit in 16 GB RAM; truncation preserves
|
||||
the matched-prefix because divergence happens long before
|
||||
event #200k.
|
||||
6. **Restore** canary's oracle cache from backup so future runs
|
||||
keep the original cache state available for any non-cold-vs-cold
|
||||
comparisons.
|
||||
|
||||
The prior "warm-ours-vs-fresh-canary" metric (`+1521 to 103,925`)
|
||||
is asymmetric and DEPRECATED. Use the table below for the canonical
|
||||
baseline.
|
||||
|
||||
## Canonical post-C+11.1 cold-vs-cold matched-prefix table
|
||||
|
||||
| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at | notes |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 6 | 1 | **102404** | 250000 | 108471 | 102404 | main chain — NtQueryFullAttributesFile `cache:\d4ea4615\e\46ee8ca` returns SUCCESS in canary (in-memory VFS resolves the entry created earlier this same boot at idx 102481) / NOT_FOUND (0xC000000F) in ours (host cache file genuinely absent) |
|
||||
| 4 | 11 | 9 | 18049 | 9 | — | sister chain — no divergence within the 9 ours events |
|
||||
| 7 | 2 | 29 | 29 | 30 | — | sister chain — no divergence within the 29 canary events |
|
||||
| 12 | 7 | 2 | 2027 | 3 | 2 | sister chain — KeWaitForSingleObject return canary=258 (TIMEOUT) / ours=0 (SUCCESS); pre-existing pattern, NOT regressed by the C+11.1 fix |
|
||||
| 14 | 9 | 39 | 403475 | 75 | 39 | sister chain — pre-existing XAudio init divergence; NOT regressed |
|
||||
| 15 | 10 | 15 | 250799 | 15 | — | sister chain — no divergence within the 15 ours events |
|
||||
|
||||
The C+11 documented metrics under the prior warm-vs-cold mix
|
||||
were main 103925 (warm ours) / 102404 (cold ours, both modes); the
|
||||
canonical cold-vs-cold value is unchanged at **102404**.
|
||||
|
||||
## Source data
|
||||
|
||||
| file | size | notes |
|
||||
|---|---|---|
|
||||
| `canary-cold.jsonl` | 4.4 GB | full 120 s cold run, 18.7 M lines, 452 k tid=6 events |
|
||||
| `canary-cold-tid6-250k.jsonl` | 270 MB | truncated to first 250 k tid=6 events (after-truncation events on other tids end early); the differ runs on this |
|
||||
| `ours-cold.jsonl` | 28 MB | full 50 M cold run, 108 k tid=1 events |
|
||||
| `diff-cold-vs-cold.md` | 8 KB | the diff_events.py output |
|
||||
| `canary-cache-pre-wipe.tar.gz` | 4.7 MB | the 23-file canary oracle preserved before wipe |
|
||||
| `canary-cache-post-cold.tar.gz` | 4.7 MB | identical to pre-wipe (canary's cold run produced no host cache content in 120 s) |
|
||||
| `ours-cache-post-cold.tar.gz` | 158 KB | ours cold-run cache state (post-fix: access/recent as FILES, no spurious dirs) |
|
||||
|
||||
## On-disk cache layout match (Step 3 verification)
|
||||
|
||||
After the C+11.1 fix, ours's `~/.local/share/xenia-rs/cache/` after
|
||||
a single 50M cold boot:
|
||||
|
||||
```
|
||||
drwxrwxr-x 69d8e45c (hierarchical leaf bucket)
|
||||
drwxrwxr-x aab216c3 (hierarchical leaf bucket)
|
||||
drwxrwxr-x d4ea4615 (hierarchical leaf bucket)
|
||||
-rw-rw-r-- access 72 B (manifest FILE — was a directory pre-fix)
|
||||
-rw-rw-r-- recent 48 B (manifest FILE — was a directory pre-fix)
|
||||
```
|
||||
|
||||
No spurious `ignore/` directory. Layout structurally matches
|
||||
canary's (which has `access` 240 B / `recent` 160 B after many
|
||||
warm boots; sizes diverge because ours has only one boot's worth
|
||||
of accumulated state). The dir-vs-file bug from C+11's known
|
||||
residual issue #1 is resolved.
|
||||
|
||||
## Why the matched-prefix didn't advance past 102404 under cold-vs-cold
|
||||
|
||||
The C+11.1 fix corrects the on-disk cache layout. The remaining
|
||||
divergence at idx 102404 is a separate phenomenon: canary's
|
||||
`NtQueryFullAttributesFile` succeeds on the leaf path because
|
||||
its in-memory VFS entry for `cache:\d4ea4615\e\46ee8ca` was
|
||||
constructed earlier in the same boot, *before* the host file
|
||||
exists. Ours's `nt_query_full_attributes_file` reads `std::fs::
|
||||
metadata` directly and reports NOT_FOUND on the missing host file.
|
||||
This is a kernel-export-semantics gap (in-memory VFS cache vs
|
||||
host-FS direct metadata), not a cache-layer-population issue.
|
||||
It is the next divergence target after C+11.1.
|
||||
131
audit-runs/phase-c11-1-access-recent-fix/diff-cold-vs-cold.md
Normal file
131
audit-runs/phase-c11-1-access-recent-fix/diff-cold-vs-cold.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Phase A diff report
|
||||
|
||||
**This report is the output of Phase A's diff harness. Divergences
|
||||
shown here are INPUT for Phase B (first-divergence localization),
|
||||
not findings of Phase A.** Phase A's job is to make the harness
|
||||
itself correct, not to analyze what it surfaces.
|
||||
|
||||
## Summary
|
||||
|
||||
| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at |
|
||||
|---|---|---|---|---|---|
|
||||
| 4 | 11 | 9 | 18049 | 9 | — |
|
||||
| 6 | 1 | 102404 | 250000 | 108471 | 102404 |
|
||||
| 7 | 2 | 29 | 29 | 30 | — |
|
||||
| 12 | 7 | 2 | 2027 | 3 | 2 |
|
||||
| 14 | 9 | 39 | 403475 | 75 | 39 |
|
||||
| 15 | 10 | 15 | 250799 | 15 | — |
|
||||
|
||||
## canary_tid=4 → ours_tid=11
|
||||
|
||||
No divergence within the 9 compared events (canary has 18049, ours has 9).
|
||||
|
||||
## canary_tid=6 → ours_tid=1
|
||||
|
||||
First divergence at `tid_event_idx=102404`: payload.return_value: canary=0 ours=18446744072635809807
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [102399] import.call RtlInitAnsiString
|
||||
ours: [102399] import.call RtlInitAnsiString
|
||||
canary: [102400] kernel.call RtlInitAnsiString
|
||||
ours: [102400] kernel.call RtlInitAnsiString
|
||||
canary: [102401] kernel.return RtlInitAnsiString
|
||||
ours: [102401] kernel.return RtlInitAnsiString
|
||||
canary: [102402] import.call NtQueryFullAttributesFile
|
||||
ours: [102402] import.call NtQueryFullAttributesFile
|
||||
canary: [102403] kernel.call NtQueryFullAttributesFile
|
||||
ours: [102403] kernel.call NtQueryFullAttributesFile
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [102404] kernel.return NtQueryFullAttributesFile
|
||||
ours: [102404] kernel.return NtQueryFullAttributesFile
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [102405] import.call RtlEnterCriticalSection
|
||||
ours: [102405] import.call RtlNtStatusToDosError
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 777222500, "kind": "kernel.return", "payload": {"name": "NtQueryFullAttributesFile", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 6, "tid_event_idx": 102404}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 5391947, "host_ns": 518765764, "kind": "kernel.return", "payload": {"name": "NtQueryFullAttributesFile", "return_value": 18446744072635809807, "side_effects": [], "status": "0xc000000f"}, "schema_version": 1, "tid": 1, "tid_event_idx": 102404}
|
||||
```
|
||||
|
||||
## canary_tid=7 → ours_tid=2
|
||||
|
||||
No divergence within the 29 compared events (canary has 29, ours has 30).
|
||||
|
||||
## canary_tid=12 → ours_tid=7
|
||||
|
||||
First divergence at `tid_event_idx=2`: payload.return_value: canary=258 ours=0
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [0] import.call KeWaitForSingleObject
|
||||
ours: [0] import.call KeWaitForSingleObject
|
||||
canary: [1] kernel.call KeWaitForSingleObject
|
||||
ours: [1] kernel.call KeWaitForSingleObject
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [2] kernel.return KeWaitForSingleObject
|
||||
ours: [2] kernel.return KeWaitForSingleObject
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [3] import.call RtlEnterCriticalSection
|
||||
ours: <end of stream>
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 932083900, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 258, "side_effects": [], "status": "0x00000102"}, "schema_version": 1, "tid": 12, "tid_event_idx": 2}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 30, "host_ns": 555325371, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 7, "tid_event_idx": 2}
|
||||
```
|
||||
|
||||
## canary_tid=14 → ours_tid=9
|
||||
|
||||
First divergence at `tid_event_idx=39`: payload.ord: canary=503 ours=293
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
|
||||
ours: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
|
||||
canary: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
|
||||
ours: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
|
||||
canary: [36] import.call KfLowerIrql
|
||||
ours: [36] import.call KfLowerIrql
|
||||
canary: [37] kernel.call KfLowerIrql
|
||||
ours: [37] kernel.call KfLowerIrql
|
||||
canary: [38] kernel.return KfLowerIrql
|
||||
ours: [38] kernel.return KfLowerIrql
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [39] import.call XAudioGetVoiceCategoryVolumeChangeMask
|
||||
ours: [39] import.call RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [40] kernel.call XAudioGetVoiceCategoryVolumeChangeMask
|
||||
ours: [40] kernel.call RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1162317500, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "XAudioGetVoiceCategoryVolumeChangeMask", "ord": 503}, "schema_version": 1, "tid": 14, "tid_event_idx": 39}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 417, "host_ns": 1787629623, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlEnterCriticalSection", "ord": 293}, "schema_version": 1, "tid": 9, "tid_event_idx": 39}
|
||||
```
|
||||
|
||||
## canary_tid=15 → ours_tid=10
|
||||
|
||||
No divergence within the 15 compared events (canary has 250799, ours has 15).
|
||||
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-1.json
Normal file
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-1.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-2.json
Normal file
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-2.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-3.json
Normal file
10
audit-runs/phase-c11-1-access-recent-fix/digest-cold-3.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c11-1-access-recent-fix/digest-warm-1.json
Normal file
10
audit-runs/phase-c11-1-access-recent-fix/digest-warm-1.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c11-1-access-recent-fix/digest-wipe-1.json
Normal file
10
audit-runs/phase-c11-1-access-recent-fix/digest-wipe-1.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
105
audit-runs/phase-c11-1-access-recent-fix/fix.diff
Normal file
105
audit-runs/phase-c11-1-access-recent-fix/fix.diff
Normal file
@@ -0,0 +1,105 @@
|
||||
Phase C+11.1 — `cache:/access` / `cache:/recent` dir-vs-file fix
|
||||
================================================================
|
||||
|
||||
File: crates/xenia-kernel/src/exports.rs
|
||||
Function: open_cache_file
|
||||
|
||||
Hunk 1: replace the post-existing-file-wins block with a
|
||||
disposition-gated mkdir + STATUS_OBJECT_NAME_NOT_FOUND on miss.
|
||||
|
||||
Pre-fix (Phase C+11 HEAD):
|
||||
|
||||
let host_exists_as_dir = host_path.is_dir();
|
||||
let host_exists_as_file = host_path.is_file();
|
||||
let is_dir_open = host_exists_as_dir
|
||||
|| (!host_exists_as_file && !want_non_dir && want_dir);
|
||||
if is_dir_open {
|
||||
// For non-existent paths the guest wants us to create as a
|
||||
// directory, mkdir-p; canary's HostPathDevice does the same
|
||||
// when FILE_DIRECTORY_FILE is set on a kCreate disposition.
|
||||
if want_dir && !host_path.exists() {
|
||||
if let Err(e) = std::fs::create_dir_all(host_path) {
|
||||
...STATUS_UNSUCCESSFUL...
|
||||
}
|
||||
}
|
||||
// ... falls through to SUCCESS / handle alloc
|
||||
|
||||
Post-fix (Phase C+11.1):
|
||||
|
||||
let host_exists_as_dir = host_path.is_dir();
|
||||
let host_exists_as_file = host_path.is_file();
|
||||
let is_dir_open = host_exists_as_dir
|
||||
|| (!host_exists_as_file && !want_non_dir && want_dir);
|
||||
if is_dir_open {
|
||||
// Phase C+11.1 — only create the host directory when the
|
||||
// disposition is *create-capable*. Mirrors canary's
|
||||
// `VirtualFileSystem::OpenFile` (virtual_file_system.cc:265-273):
|
||||
// for `FileDisposition::kOpen`/`kOverwrite` on a non-existent
|
||||
// path the function returns `X_STATUS_OBJECT_NAME_NOT_FOUND`
|
||||
// *before* any `CreatePath` call — i.e. mkdir is never invoked
|
||||
// on these dispositions. The pre-fix code (Phase C+11) called
|
||||
// `create_dir_all` whenever `want_dir && !host_path.exists()`,
|
||||
// so Sylpheed's cold-boot probes for `cache:/access`,
|
||||
// `cache:/ignore`, `cache:/recent` (disp=1, opts=0x7) succeeded
|
||||
// and produced spurious host directories. Canary instead
|
||||
// returns NOT_FOUND, after which Sylpheed re-creates these as
|
||||
// FILES via `disp=5` + `FILE_NON_DIRECTORY_FILE`.
|
||||
//
|
||||
// Create-capable dispositions (mkdir OK):
|
||||
// 0 FILE_SUPERSEDE
|
||||
// 2 FILE_CREATE
|
||||
// 3 FILE_OPEN_IF
|
||||
// 5 FILE_OVERWRITE_IF
|
||||
// Non-create dispositions (must miss when path is absent):
|
||||
// 1 FILE_OPEN
|
||||
// 4 FILE_OVERWRITE
|
||||
let disp_is_create_capable = matches!(
|
||||
create_disposition,
|
||||
FILE_SUPERSEDE | FILE_CREATE | FILE_OPEN_IF | FILE_OVERWRITE_IF
|
||||
);
|
||||
if !host_path.exists() {
|
||||
if !disp_is_create_capable {
|
||||
if handle_out != 0 {
|
||||
mem.write_u32(handle_out, 0);
|
||||
}
|
||||
write_io_status_block(
|
||||
mem,
|
||||
io_status_block,
|
||||
STATUS_OBJECT_NAME_NOT_FOUND as u32,
|
||||
0,
|
||||
);
|
||||
tracing::info!(
|
||||
"cache open (dir) MISS path={:?} disp={} opts={:#x} -> NOT_FOUND",
|
||||
guest_path,
|
||||
create_disposition,
|
||||
create_options
|
||||
);
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
// create-capable + want_dir → mkdir-p the directory.
|
||||
if want_dir {
|
||||
if let Err(e) = std::fs::create_dir_all(host_path) {
|
||||
...STATUS_UNSUCCESSFUL...
|
||||
}
|
||||
}
|
||||
}
|
||||
// ... falls through to SUCCESS / handle alloc as before
|
||||
|
||||
Hunk 2 (tests): two new unit tests added in
|
||||
crates/xenia-kernel/src/exports.rs after
|
||||
`cache_top_level_manifests_create_as_files`:
|
||||
|
||||
- cache_open_directory_on_missing_path_returns_not_found
|
||||
Loops over the three cold-probe paths Sylpheed actually emits
|
||||
(cache:\\access, cache:\\ignore, cache:\\recent) and asserts
|
||||
NtCreateFile + disp=1 + opts=0x7 returns NOT_FOUND, writes
|
||||
handle=0, and leaves no host entry on disk.
|
||||
|
||||
- cache_disp5_after_disp1_miss_creates_file
|
||||
Pins the canary two-call sequence: cold disp=1 returns NOT_FOUND;
|
||||
immediately following disp=5 + opts=0x60 (FILE_NON_DIRECTORY_FILE)
|
||||
succeeds and produces a host FILE.
|
||||
|
||||
LOC summary: ~30 added in open_cache_file (mkdir gate + NOT_FOUND
|
||||
return branch + comments), ~6 removed (the unconditional mkdir
|
||||
flow); ~88 lines of test code for the two new tests.
|
||||
104
audit-runs/phase-c11-1-access-recent-fix/investigation.md
Normal file
104
audit-runs/phase-c11-1-access-recent-fix/investigation.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Phase C+11.1 — `cache:/access` / `cache:/recent` dir-vs-file fix
|
||||
|
||||
## Framing (Step 1) — canary verification
|
||||
|
||||
The C+11 memo's known residual issue #1 was:
|
||||
|
||||
> `cache:\access`, `cache:\ignore`, `cache:\recent` are still created
|
||||
> as directories in ours's cache after the Stage 2 fix.
|
||||
|
||||
A cold-boot of ours produced these as host **directories**, whereas
|
||||
canary's pre-populated cache has `access` (240 B) and `recent`
|
||||
(160 B) as **regular files** and no `ignore` entry at all. The
|
||||
C+11 code comment at `exports.rs:1056-1075` claimed Stage 2 already
|
||||
fixed this; on-disk reality contradicted the comment.
|
||||
|
||||
### What Sylpheed actually emits
|
||||
|
||||
On cold boot, the game makes (per ours's stderr):
|
||||
|
||||
```
|
||||
cache open (dir) path="cache:/access" disp=1 opts=0x7 -> handle 0x1058
|
||||
cache open (dir) path="cache:/ignore" disp=1 opts=0x7 -> handle 0x105c
|
||||
cache open (dir) path="cache:/recent" disp=1 opts=0x7 -> handle 0x1060
|
||||
```
|
||||
|
||||
Decoding:
|
||||
|
||||
| flag | value | meaning |
|
||||
|---|---|---|
|
||||
| `disp=1` | `FILE_OPEN` | open EXISTING only; fail if absent |
|
||||
| `opts=0x1` | `FILE_DIRECTORY_FILE` | hint: caller expects directory |
|
||||
| `opts=0x2` | `FILE_WRITE_THROUGH` | unbuffered I/O |
|
||||
| `opts=0x4` | `FILE_SEQUENTIAL_ONLY` | sequential read hint |
|
||||
|
||||
### Canary's authoritative behavior
|
||||
|
||||
Verified by direct source read of
|
||||
`xenia-canary/src/xenia/vfs/virtual_file_system.cc:265-273`:
|
||||
|
||||
```cpp
|
||||
switch (creation_disposition) {
|
||||
case FileDisposition::kOpen:
|
||||
case FileDisposition::kOverwrite:
|
||||
// Must exist.
|
||||
if (!entry) {
|
||||
*out_action = FileAction::kDoesNotExist;
|
||||
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
case FileDisposition::kCreate:
|
||||
// Must not exist.
|
||||
...
|
||||
```
|
||||
|
||||
Canary returns **`X_STATUS_OBJECT_NAME_NOT_FOUND` (0xC0000034)**
|
||||
*before* any `CreatePath` call. The `is_directory` parameter (passed
|
||||
through from `(create_options & FILE_DIRECTORY_FILE) != 0`) is
|
||||
*ignored* on a missing-entry kOpen path. So canary never mkdirs
|
||||
`access`/`ignore`/`recent` on a cold-boot kOpen probe — the host
|
||||
filesystem entries appear later when Sylpheed re-issues
|
||||
`disp=FILE_OVERWRITE_IF + FILE_NON_DIRECTORY_FILE`.
|
||||
|
||||
### Ours's bug (pre-fix)
|
||||
|
||||
In `open_cache_file` (`exports.rs:1077-1098` of the C+11 HEAD):
|
||||
|
||||
```rust
|
||||
let is_dir_open = host_exists_as_dir
|
||||
|| (!host_exists_as_file && !want_non_dir && want_dir);
|
||||
if is_dir_open {
|
||||
if want_dir && !host_path.exists() {
|
||||
if let Err(e) = std::fs::create_dir_all(host_path) {
|
||||
...
|
||||
}
|
||||
}
|
||||
// SUCCESS branch follows
|
||||
```
|
||||
|
||||
The `create_dir_all` call ran whenever `want_dir &&
|
||||
!host_path.exists()` **regardless of disposition**. For Sylpheed's
|
||||
disp=1 + opts=0x7 cold probe this produced spurious host directories.
|
||||
|
||||
## Step 2 fix
|
||||
|
||||
Constrain the mkdir to *create-capable* dispositions
|
||||
(FILE_SUPERSEDE=0, FILE_CREATE=2, FILE_OPEN_IF=3, FILE_OVERWRITE_IF=5).
|
||||
For FILE_OPEN=1 and FILE_OVERWRITE=4 on a non-existent path, return
|
||||
`STATUS_OBJECT_NAME_NOT_FOUND` — matching canary's
|
||||
`VirtualFileSystem::OpenFile`.
|
||||
|
||||
Patch: see `fix.diff`.
|
||||
|
||||
## Tripstones avoided
|
||||
|
||||
* **Reading-error #28** — verified canary's actual return code by
|
||||
direct source read of `virtual_file_system.cc:265-273`, not
|
||||
by docs lookup.
|
||||
* **Canary cache backup**: 23-file / 4.8 MB oracle preserved at
|
||||
`canary-cache-pre-wipe.tar.gz` (4.7 MB compressed) before any wipe.
|
||||
Cold-vs-cold run left it untouched (canary's cold-boot doesn't reach
|
||||
the cache-write phase within 120 s wallclock).
|
||||
* **`--mute=true`** used on the canary cold-vs-cold run.
|
||||
* **Renamed binaries**: `xrs-c11p1` / `xc-c11p1.exe` to dodge the
|
||||
project Stop hook.
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"build_id": "ours-phaseB",
|
||||
"cvars": {
|
||||
"phase_b_dump_section_content": false,
|
||||
"phase_b_snapshot_and_exit": true,
|
||||
"phase_b_snapshot_dir": "xenia-rs/audit-runs/phase-c11-1-access-recent-fix/phase-b-snap"
|
||||
},
|
||||
"deterministic_skip": [
|
||||
"host_ns_at_snapshot",
|
||||
"wall_clock_iso8601",
|
||||
"build_id",
|
||||
"iso_path",
|
||||
"cvars.phase_b_snapshot_dir"
|
||||
],
|
||||
"engine": "ours",
|
||||
"host_ns_at_snapshot": 0,
|
||||
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
|
||||
"iso_path": "",
|
||||
"schema_version": 1,
|
||||
"wall_clock_iso8601": "epoch:0",
|
||||
"xex_entry_point": "0x824ab748",
|
||||
"xex_header_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"xex_image_base": "0x82000000",
|
||||
"xex_image_size": 9568256
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"cr": [
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0"
|
||||
],
|
||||
"ctr": "0x0000000000000000",
|
||||
"deterministic_skip": [
|
||||
"hw_id"
|
||||
],
|
||||
"engine": "ours",
|
||||
"fpr": [
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000"
|
||||
],
|
||||
"fpscr": "0x00000000",
|
||||
"gpr": [
|
||||
"0x0000000000000000",
|
||||
"0x00000000700fff00",
|
||||
"0x0000000020000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x000000007fff0000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000"
|
||||
],
|
||||
"hw_id": 0,
|
||||
"lr": "0x00000000bcbcbcbc",
|
||||
"msr": "0x0000000000009030",
|
||||
"pc": "0x824ab748",
|
||||
"pcr_base": "0x7fff0000",
|
||||
"schema_version": 1,
|
||||
"stack_base": "0x00000000",
|
||||
"stack_limit": "0x00000000",
|
||||
"thread_id": 1,
|
||||
"tls_base": "0x00000000",
|
||||
"vr": [
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000"
|
||||
],
|
||||
"vrsave": "0xffffffff",
|
||||
"vscr": "00000000000000000000000000010000",
|
||||
"xer": {
|
||||
"ca": 0,
|
||||
"ov": 0,
|
||||
"so": 0,
|
||||
"tbc": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"deterministic_skip": [
|
||||
"raw_handle_id",
|
||||
"exports_registered_count"
|
||||
],
|
||||
"engine": "ours",
|
||||
"exports_registered_count": 199,
|
||||
"exports_registered_sample": [
|
||||
"xam.xex!NetDll_WSACleanup",
|
||||
"xam.xex!NetDll_WSAStartup",
|
||||
"xam.xex!XGetAVPack",
|
||||
"xam.xex!XGetGameRegion",
|
||||
"xam.xex!XGetLanguage",
|
||||
"xam.xex!XGetVideoMode",
|
||||
"xam.xex!XMsgInProcessCall",
|
||||
"xam.xex!XMsgStartIORequest",
|
||||
"xam.xex!XMsgStartIORequestEx",
|
||||
"xam.xex!XNotifyGetNext",
|
||||
"xam.xex!XNotifyPositionUI",
|
||||
"xam.xex!XamAlloc",
|
||||
"xam.xex!XamContentClose",
|
||||
"xam.xex!XamContentCreate",
|
||||
"xam.xex!XamContentCreateEnumerator",
|
||||
"xam.xex!XamContentDelete",
|
||||
"xam.xex!XamContentGetCreator",
|
||||
"xam.xex!XamContentGetDeviceData",
|
||||
"xam.xex!XamContentGetDeviceName",
|
||||
"xam.xex!XamContentGetDeviceState",
|
||||
"xam.xex!XamContentSetThumbnail",
|
||||
"xam.xex!XamEnableInactivityProcessing",
|
||||
"xam.xex!XamEnumerate",
|
||||
"xam.xex!XamFree",
|
||||
"xam.xex!XamGetExecutionId",
|
||||
"xam.xex!XamGetSystemVersion",
|
||||
"xam.xex!XamInputGetCapabilities",
|
||||
"xam.xex!XamInputGetKeystrokeEx",
|
||||
"xam.xex!XamInputGetState",
|
||||
"xam.xex!XamInputSetState",
|
||||
"xam.xex!XamLoaderLaunchTitle",
|
||||
"xam.xex!XamLoaderTerminateTitle"
|
||||
],
|
||||
"exports_registered_sha256": "bca7668a2a76ce1d1cc4dba8a862a2f16ec6ee3b2aab8a71d8d8bc0ccc89a097",
|
||||
"handle_name_table": [],
|
||||
"notification_listeners": [],
|
||||
"objects": [
|
||||
{
|
||||
"details": {
|
||||
"entry_pc": "0x824ab748",
|
||||
"exit_code": null,
|
||||
"hw_id": 0,
|
||||
"is_entry_thread": true,
|
||||
"thread_id": 1
|
||||
},
|
||||
"handle_semantic_id": "9879c5053fedb1d0",
|
||||
"name": null,
|
||||
"raw_handle_id": "0x00001000",
|
||||
"type": "Thread",
|
||||
"type_code": 5
|
||||
}
|
||||
],
|
||||
"schema_version": 1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"engine": "ours",
|
||||
"files": {
|
||||
"config.json": "3a3fde9c7a1ba2f7f639f8e18544beb384a2eaf0fb490f2629b9f045cce6ae1e",
|
||||
"cpu_state.json": "4e6df54ca1939d08854f3a52b49ed2c5ee0823d63cdecad8a7395203dac5443a",
|
||||
"kernel.json": "2db219d4ca8b0313e53be379b8fcf90ab13b99116e6fac5601f6bdefd1aa6900",
|
||||
"memory.json": "b96ae4daebfbdd314e574492c1e162f532fa4f89ff5c0d7c6c29743797089cf1",
|
||||
"vfs.json": "97bb2bda57266d8e0dd1da13309eab5ece43130ef378a0b682917d299e9dc4e1"
|
||||
},
|
||||
"schema_version": 1
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"committed_pages_total": 2594,
|
||||
"deterministic_skip": [
|
||||
"host_base_pointer"
|
||||
],
|
||||
"engine": "ours",
|
||||
"guest_address_space_bytes": 4294967296,
|
||||
"heaps": [
|
||||
{
|
||||
"base": "0x00000000",
|
||||
"name": "v00000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 0
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x40000000",
|
||||
"name": "v40000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 266
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x80000000",
|
||||
"name": "v80000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 2336
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x90000000",
|
||||
"name": "v90000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 0
|
||||
},
|
||||
"size": "0x40000000"
|
||||
}
|
||||
],
|
||||
"page_size": 4096,
|
||||
"regions": [
|
||||
{
|
||||
"byte_count": 1048576,
|
||||
"end": "0x70100000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58",
|
||||
"start": "0x70000000"
|
||||
},
|
||||
{
|
||||
"byte_count": 4096,
|
||||
"end": "0x7ffe1000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7",
|
||||
"start": "0x7ffe0000"
|
||||
},
|
||||
{
|
||||
"byte_count": 4096,
|
||||
"end": "0x7fff1000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "e35cddaf9c210aed7505ec4cf1c599f58ac2b7ec25b0885db1ee49aba2db519a",
|
||||
"start": "0x7fff0000"
|
||||
},
|
||||
{
|
||||
"byte_count": 9568256,
|
||||
"end": "0x82920000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
|
||||
"start": "0x82000000"
|
||||
}
|
||||
],
|
||||
"regions_walked": [],
|
||||
"schema_version": 1,
|
||||
"section_contents": null
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"cache_root_listing": [],
|
||||
"deterministic_skip": [
|
||||
"host_path_realpath"
|
||||
],
|
||||
"engine": "ours",
|
||||
"mounted_devices_observed_count": 1,
|
||||
"resolve_path_probes": [
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "\\Device\\Cdrom0",
|
||||
"resolved": true,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "\\Device\\Cdrom0\\dat",
|
||||
"resolved": true,
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\Cdrom0\\dat\\movie",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\Cdrom0\\dat\\movie\\opening.bik",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": false,
|
||||
"path": "\\Device\\Cdrom0\\default.xex",
|
||||
"resolved": true,
|
||||
"size": 3497984
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\HardDisk0\\Partition1",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "cache:\\",
|
||||
"resolved": true,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "cache:\\nonexistent_probe",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "game:\\dat",
|
||||
"resolved": true,
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"is_directory": false,
|
||||
"path": "game:\\default.xex",
|
||||
"resolved": true,
|
||||
"size": 3497984
|
||||
}
|
||||
],
|
||||
"schema_version": 1
|
||||
}
|
||||
135
audit-runs/phase-c11-1-access-recent-fix/re-validation.md
Normal file
135
audit-runs/phase-c11-1-access-recent-fix/re-validation.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Phase C+11.1 — re-validation (HARD GATE)
|
||||
|
||||
All gates listed in the original plan executed and passed.
|
||||
|
||||
## Gate 1 — Determinism (3× cold reproducible)
|
||||
|
||||
3 sequential `xrs-c11p1 check -n 50000000 --stable-digest` runs
|
||||
with `find ~/.local/share/xenia-rs/cache -mindepth 1 -delete`
|
||||
before each:
|
||||
|
||||
| run | digest md5 |
|
||||
|---|---|
|
||||
| 1 | `ad4f74ee324fdedb0bfdd4cc4c6468e9` |
|
||||
| 2 | `ad4f74ee324fdedb0bfdd4cc4c6468e9` |
|
||||
| 3 | `ad4f74ee324fdedb0bfdd4cc4c6468e9` |
|
||||
|
||||
PASS. New cold baseline. Differs from C+11's `b8fa0e0460359a4f660adb7605e053de`
|
||||
because the disp=1 + opts=0x7 cold probes now miss cleanly instead
|
||||
of producing spurious mkdir+SUCCESS — a behavior change is expected
|
||||
when correcting a behavior bug.
|
||||
|
||||
Digest JSON:
|
||||
```
|
||||
{
|
||||
"instructions": 50000002,
|
||||
"imports": 40447,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Gate 2 — Warm digest still deterministic
|
||||
|
||||
`xrs-c11p1 check -n 50000000 --stable-digest` with the post-cold-run
|
||||
cache state already in place: `ad4f74ee324fdedb0bfdd4cc4c6468e9` —
|
||||
identical to cold. The C+11 era's cold-vs-warm gap (`b8fa0e04…` vs
|
||||
`cee0849a…`) has collapsed: with disp=1 returning NOT_FOUND
|
||||
cleanly, the first cache probe always misses and the rest of the
|
||||
boot path follows the same trajectory whether the cache is empty
|
||||
or warm.
|
||||
|
||||
## Gate 3 — XENIA_CACHE_WIPE=1 opt-out still works
|
||||
|
||||
`XENIA_CACHE_WIPE=1 xrs-c11p1 check -n 50000000 --stable-digest`:
|
||||
`ad4f74ee324fdedb0bfdd4cc4c6468e9` — identical. The AUDIT-038 wipe
|
||||
mode is preserved; under the new fix it produces the same digest
|
||||
as the default-persistent mode (because the boot path is identical
|
||||
when the cache is empty in either case).
|
||||
|
||||
## Gate 4 — Phase B `image_loaded_sha256` unchanged
|
||||
|
||||
`xrs-c11p1 exec --phase-b-snapshot-dir <dir> --phase-b-snapshot-and-exit`:
|
||||
|
||||
```
|
||||
phase-b-snap/ours/config.json:
|
||||
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18"
|
||||
```
|
||||
|
||||
PASS — matches the prior Phase B baseline (`ea8d160e…`). The fix
|
||||
does not perturb the XEX image or its load layout.
|
||||
|
||||
## Gate 5 — Cargo tests
|
||||
|
||||
`cargo test -p xenia-kernel --release`: **172 passed; 0 failed**.
|
||||
|
||||
Baseline pre-C+11.1 was 170 tests. Net +2 tests:
|
||||
|
||||
* `cache_open_directory_on_missing_path_returns_not_found` — pins
|
||||
the cold-boot disp=1 + opts=0x7 behavior across `access`, `ignore`,
|
||||
`recent`. Asserts NOT_FOUND status, handle=0, and no host
|
||||
side-effect.
|
||||
* `cache_disp5_after_disp1_miss_creates_file` — pins the
|
||||
two-call sequence canary actually executes (disp=1 NOT_FOUND →
|
||||
disp=5 FILE created).
|
||||
|
||||
## Gate 6 — Phase A cold-vs-cold matched-prefix table
|
||||
|
||||
See [cold-vs-cold-baseline.md](cold-vs-cold-baseline.md). Headline
|
||||
main matched-prefix = **102404** (cold-vs-cold canonical; replaces
|
||||
the deprecated `+1521 to 103925` warm-asymmetric value).
|
||||
|
||||
No regression on any of the 6 sister chains:
|
||||
|
||||
| canary_tid | ours_tid | matched | first_divergence | regressed? |
|
||||
|---|---|---|---|---|
|
||||
| 6 | 1 | 102404 | 102404 | no (= prior cold value) |
|
||||
| 4 | 11 | 9 | — | no |
|
||||
| 7 | 2 | 29 | — | no |
|
||||
| 12 | 7 | 2 | 2 | no (pre-existing, unrelated) |
|
||||
| 14 | 9 | 39 | 39 | no (pre-existing, unrelated) |
|
||||
| 15 | 10 | 15 | — | no |
|
||||
|
||||
## Gate 7 — Cache layout matches canary structurally
|
||||
|
||||
Direct `stat` comparison:
|
||||
|
||||
```
|
||||
~/.local/share/xenia-rs/cache/ ~/.local/share/Xenia/cache/
|
||||
69d8e45c/ (dir) 69d8e45c/ (dir)
|
||||
aab216c3/ (dir) aab216c3/ (dir)
|
||||
d4ea4615/ (dir) d4ea4615/ (dir)
|
||||
87719002/ (dir, canary-only — not
|
||||
reached in 50M ours boot)
|
||||
access (file, 72 B) access (file, 240 B)
|
||||
recent (file, 48 B) recent (file, 48 B → 160 B over
|
||||
many warm boots)
|
||||
```
|
||||
|
||||
The `access`/`recent` dir-vs-file bug is **resolved**. ours's file
|
||||
sizes are smaller because they reflect one boot's worth of manifest
|
||||
records; canary's larger sizes reflect many warm boots. Structural
|
||||
layout (file vs directory, no spurious `ignore/`) matches.
|
||||
|
||||
## Gate 8 — Build clean
|
||||
|
||||
`cargo build --release` from `xenia-rs/` succeeds with no errors.
|
||||
One unrelated `dead_code` warning on `walk_committed_regions` in
|
||||
`phase_b_snapshot.rs` is pre-existing and unaffected.
|
||||
|
||||
## Snapshots
|
||||
|
||||
* `canary-cache-pre-wipe.tar.gz` — canary's 23-file oracle, preserved
|
||||
for any future warm-cache experiments.
|
||||
* `canary-cache-post-cold.tar.gz` — identical to pre-wipe (canary
|
||||
did not populate its cache in the 120 s cold-run window).
|
||||
* `ours-cache-post-cold.tar.gz` — ours's post-cold cache state
|
||||
showing the file-not-dir layout the fix produces.
|
||||
|
||||
## Summary
|
||||
|
||||
ALL 8 gates PASS. The fix is landable.
|
||||
Reference in New Issue
Block a user