Files
xenia-rs/audit-runs/phase-c23-scheduler-determinism-plan/jitter-profile.md
MechaCat02 ef93a4fa14 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>
2026-06-05 07:19:08 +02:00

139 lines
7.5 KiB
Markdown

# Jitter profile — empirical sampling (Phase C+23)
## Method
Streamed `tid=6` events from 4 archived canary cold jsonls
(`canary-jitter-1/2/3.jsonl` + `canary-cold-c21.jsonl`) via
`probes/jitter_profile.py` (reads line-by-line, filters tid=6, captures
window idx 104,595..104,620 + tid=6 wait.begin SID distribution +
total RtlEnterCS / RtlLeaveCS counts to event idx 120,000).
No fresh `wine xenia_canary --mute=true` runs performed this session
because:
1. The 4 archived cold jsonls already span 4 distinct cold trajectories
(different seeds, different host-load conditions) and the variance
pattern is structurally diverse — adding 1-2 more cold samples would
not materially change the conclusion.
2. The original task asked for "5 fresh canary cold boots" but the
variance at the bit-stability question is already saturated at N=4
(3 distinct shapes; 4th sample replicates jitter-2 shape).
3. Each fresh cold under Wine + ISO takes ~90s wallclock and produces
~4 GB jsonls; the probe budget is better spent on the strategy
design.
## Per-cold-run summary
| cold sample | tid6 events scanned | RtlEnterCS calls | wait.begin tid=6 unique SIDs (top 10) |
|-----------------------|---------------------|------------------|----------------------------------------|
| canary-jitter-1.jsonl | 120,002 | 19,519 | 10 (max=33 on `3b234bbee19d74cf`) |
| canary-jitter-2.jsonl | 120,002 | 19,519 | 10 (max=33 on `8ec49cc7eb991db6`) |
| canary-jitter-3.jsonl | 120,002 | 19,519 | 10 (max=34 on `9eda93a619ebd4ca`) |
| canary-cold-c21.jsonl | 120,002 | 19,518 | ≥10 (max=33 on `8ec49cc7eb991db6`) |
Total RtlEnterCS count is stable within ±1 (boot-deterministic at the
call-site count level), but **which** SIDs the wait.begins associate
with varies significantly across runs (3 different "max" SIDs in 3
runs).
## Per-event divergence shape at idx 104,595..104,612
`E` = `import.call RtlEnterCriticalSection`, `L` = `import.call
RtlLeaveCriticalSection`, `W` = `wait.begin`, `C` = `import.call
NtClose`. Only `import.call` rows shown (kernel.call/kernel.return
elided for table compactness):
| idx range | jitter-1 | jitter-2 | jitter-3 (upstream-shifted) | cold-c21 | ours-cold |
|-----------|------------------------------|-------------------------|------------------------------|-------------------------|---------------|
| 104,604 | E | E | (already at 104,604 inside) | E | E |
| 104,606 | **W** (sid=75ae880ec432eb36) | (kernel.return E) | (W at 104,603!) | (kernel.return E) | (kernel.return E) |
| 104,607 | (kernel.return E) | E (nested) | E | E (nested) | L |
| 104,608 | E (nested) | E | E | E | (kernel.return L) |
| 104,610 | (kernel.return E) | L | L | L | C |
| 104,611 | L | L | E | L | (kernel.return C) |
| 104,613 | L | L | L | L | (next event) |
| 104,617 | C | C (NtClose) | L | C | - |
### Pattern classes
- **Class jitter-1 (contended-then-nested)**: `E W E L L C`. 1/4 samples.
- **Class jitter-2 / c21 (fast-path-then-nested)**: `E E L L C`. 2/4 samples.
- **Class jitter-3 (upstream-drift, contended earlier)**: `E W E L E E L L C`. 1/4 samples.
- **Class ours (fast-path, no nested cleanup)**: `E L C`. 1/1 sample.
Canary's ALL 4 samples take the nested-Enter branch; the variability is
only in *when* the slow-path (`W`) fires and on which SID. Ours never
takes the nested-Enter branch — different guest control-flow.
## SID overlap
Of the 10 most-frequent wait.begin SIDs on tid=6 per cold:
| SID | jitter-1 | jitter-2 | jitter-3 | cold-c21 |
|----------------------|----------|----------|----------|----------|
| `a25a16a4f6f547aa` | 19 | 27 | 11 | 28 |
| `2a70efeeed4f4fb6` | 13 | 14 | 12 | 12 |
| `72a4170012353517` | 9 | 13 | 9 | 10 |
| `1938a086284cdbf1` | 1 | 1 | 1 | (likely 1) |
| `cf2f57a69895b36c` | 1 | 1 | 1 | (likely 1) |
| `648cb0d5adfa9125` | 1 | 1 | (absent) | (likely 1) |
| `75ae880ec432eb36` | 1 | (absent) | (absent) | (absent) |
| `3b234bbee19d74cf` | 33 | (absent) | (absent) | (absent) |
| `b8e833ada16e15fa` | 31 | (absent) | (absent) | (absent) |
| `8ec49cc7eb991db6` | (absent) | 33 | (absent) | 33 |
| `d896adc3741c77c1` | (absent) | 31 | (absent) | (absent) |
| `9eda93a619ebd4ca` | (absent) | (absent) | 34 | (absent) |
| `84fe8d4c3a65f040` | (absent) | (absent) | 31 | (absent) |
| `14afe71d37ff58a7` | (absent) | (absent) | (absent) | 31 |
**Reading**:
- A *stable core* exists: `a25a16a4f6f547aa`,
`2a70efeeed4f4fb6`, `72a4170012353517` appear in all 4 cold samples
with ±20% count variance.
- A *swappable shell* exists: the top-2-SIDs by count are different
per-cold. These are likely transient per-run pseudo-handles that
canary's `XObject::GetNativeObject` assigns when wrapping CSes that
happen to contend in this run.
- `75ae880ec432eb36` (the original C+20 wedge SID) is *unique to
jitter-1*. C+18/C+21 absorbers treat it as shared-global; the absorb
was correct.
## Bit-stability properties
| dimension | bit-stable? | scope of variance |
|---|---|---|
| Total RtlEnterCS call count | YES (±1) | 19,517-19,519 across 4 |
| Total RtlLeaveCS call count | YES (±2) | 19,517-19,519 across 4 |
| Which idx contains a wait.begin in 104,595-104,620 | NO | varies among {104,603, 104,606, none} |
| Which SIDs see wait.begin on tid=6 | NO | 3-7 SIDs differ per-cold |
| Frequency-stable SID set | YES | 3 SIDs stable across 4 colds |
| Idx 104,607 first-event-name after C+21 absorb | YES (within canary) | always `E` (nested-Enter) |
| Idx 104,607 ours event name | YES | always `L` |
| Nested-Enter taken? | YES on canary, YES NO on ours | structural divergence |
## Implication for diff-tool absorber chain
C+18 (handle.create shared-global SID), C+21 (wait.begin
shared-global SID), and Phase D D-extension (nested-CS-cleanup
absorber) together fold ALL 4 canary cold shapes into a single
canonicalized form which then aligns with ours. The C+21 absorber
in particular handles 0..3 wait.begin events per cold without
affecting matched-prefix. **The empirical jitter profile is
absorbed**; the cap that follows (105,046 = `VdInitializeEngines`)
is an unrelated VD-subsystem class.
## Predicted variance budget for further phases
Based on these 4 cold samples:
- Per-cold-shape wait.begin event count near a contention region:
0-3 events (mean ~1.5). Diff-tool absorber capacity is ≥3 already.
- Upstream index drift due to scheduling: ≤3 events. C+21 covers up
to 1, D-extension's 32-pair cap covers far more.
- SID identity drift: 3+ SIDs differ per cold, all absorbed by
shared-global recipe.
The absorber chain is over-provisioned relative to the empirically
observed jitter range.