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>
147 lines
5.6 KiB
Python
147 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Extract canary tid=6 timeline at and just before the same point ours wedges.
|
|
|
|
The matched-prefix endpoint is when ours's tid=1 calls
|
|
NtWaitForSingleObjectEx on tid=13.handle at host_ns=1.727s.
|
|
|
|
In canary, tid=6's analog wait is the sub_82173990 KeWaitForSingleObject
|
|
INFINITE — but in canary it completes when the spawned worker (tid=17 =
|
|
sub_821748F0 body) terminates. Need to find that wait in canary's stream.
|
|
|
|
Output: ordered timeline of canary tid=6 from spawn-of-sub_821748F0
|
|
through install-epoch.
|
|
"""
|
|
import json
|
|
import os
|
|
from collections import Counter
|
|
|
|
INPUT = "/home/fabi/RE - Project Sylpheed/xenia-canary/build-cross/bin/Windows/Debug/canary-jitter-1.jsonl"
|
|
OUTDIR = os.path.dirname(os.path.abspath(__file__))
|
|
TARGET_TID = 6
|
|
|
|
# Capture canary tid=6 from t=1.5s through t=11s (sub_821748F0 spawn through worker fan-out).
|
|
T_LO = 1_500_000_000
|
|
T_HI = 11_000_000_000
|
|
|
|
kernel_calls = []
|
|
kernel_returns = []
|
|
import_calls = []
|
|
handle_creates = []
|
|
thread_events = []
|
|
wait_events = []
|
|
other = []
|
|
|
|
with open(INPUT, "r") as f:
|
|
for line in f:
|
|
if '"host_ns":' not in line:
|
|
continue
|
|
try:
|
|
i = line.index('"host_ns":') + len('"host_ns":')
|
|
j = i
|
|
while j < len(line) and (line[j].isdigit() or line[j] == '-'):
|
|
j += 1
|
|
host_ns = int(line[i:j])
|
|
except (ValueError, IndexError):
|
|
continue
|
|
if host_ns < T_LO:
|
|
continue
|
|
if host_ns >= T_HI:
|
|
break
|
|
# Quick tid filter.
|
|
if f'"tid":{TARGET_TID},' not in line:
|
|
continue
|
|
try:
|
|
ev = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
if ev.get("tid") != TARGET_TID:
|
|
continue
|
|
kind = ev.get("kind", "")
|
|
if kind == "kernel.call":
|
|
kernel_calls.append(ev)
|
|
elif kind == "kernel.return":
|
|
kernel_returns.append(ev)
|
|
elif kind == "handle.create":
|
|
handle_creates.append(ev)
|
|
elif kind in ("thread.create", "thread.exit"):
|
|
thread_events.append(ev)
|
|
elif kind == "import.call":
|
|
import_calls.append(ev)
|
|
elif kind in ("wait.begin", "wait.end", "wait.wake"):
|
|
wait_events.append(ev)
|
|
else:
|
|
other.append(ev)
|
|
|
|
print(f"canary tid={TARGET_TID} in window [{T_LO/1e9}..{T_HI/1e9}s]")
|
|
print(f" kernel.call: {len(kernel_calls)}")
|
|
print(f" kernel.return: {len(kernel_returns)}")
|
|
print(f" handle.create: {len(handle_creates)}")
|
|
print(f" thread.create/exit: {len(thread_events)}")
|
|
print(f" import.call: {len(import_calls)}")
|
|
print(f" wait.* {len(wait_events)}")
|
|
|
|
# Save full timeline.
|
|
all_evts = []
|
|
for ev in kernel_calls + kernel_returns + handle_creates + thread_events + wait_events:
|
|
all_evts.append((ev["host_ns"], ev["tid_event_idx"], ev["kind"], ev["payload"]))
|
|
all_evts.sort()
|
|
|
|
# Find anchor points:
|
|
# 1. ExCreateThread with entry=0x821748f0 (the matched spawn site).
|
|
# 2. NtWaitForSingleObjectEx on the resulting handle (the analog of ours's wedge).
|
|
# 3. Wait return time.
|
|
# 4. Subsequent calls that lead to sub_825070F0 fan-out at host_ns ~10.383s.
|
|
|
|
print("\n=== Looking for anchor: ExCreateThread on entry 0x821748f0 ===")
|
|
anchor_idx = -1
|
|
anchor_ns = -1
|
|
anchor_handle = None
|
|
for i, (host_ns, idx, kind, payload) in enumerate(all_evts):
|
|
if kind == "thread.create":
|
|
entry = payload.get("entry_pc", "")
|
|
if entry == "0x821748f0" or entry == "0x821748F0":
|
|
print(f" Found at idx={idx} host_ns={host_ns} ({host_ns/1e9:.3f}s)")
|
|
print(f" payload={json.dumps(payload)}")
|
|
anchor_idx = i
|
|
anchor_ns = host_ns
|
|
anchor_handle = payload.get("handle_semantic_id")
|
|
break
|
|
|
|
# Locate the next NtWaitForSingleObjectEx on tid=6 - that's the join wait.
|
|
print("\n=== Finding the join-wait on tid=6 after ExCreateThread ===")
|
|
for i in range(anchor_idx, min(anchor_idx + 200, len(all_evts))):
|
|
host_ns, idx, kind, payload = all_evts[i]
|
|
if kind == "wait.begin":
|
|
if anchor_handle and anchor_handle in payload.get("handles_semantic_ids", []):
|
|
print(f" Join wait.begin at idx={idx} host_ns={host_ns} ({host_ns/1e9:.3f}s)")
|
|
print(f" timeout_ns={payload.get('timeout_ns')}")
|
|
join_wait_start_ns = host_ns
|
|
join_wait_start_eidx = i
|
|
break
|
|
elif kind == "kernel.call" and payload.get("name") == "KeWaitForSingleObject":
|
|
print(f" KeWait at idx={idx} host_ns={host_ns} ({host_ns/1e9:.3f}s)")
|
|
|
|
# Look for wait.end / wait.wake.
|
|
print("\n=== Finding the join-wait completion ===")
|
|
for i in range(anchor_idx, len(all_evts)):
|
|
host_ns, idx, kind, payload = all_evts[i]
|
|
if kind in ("wait.end", "wait.wake"):
|
|
if anchor_handle and anchor_handle in payload.get("handles_semantic_ids", []):
|
|
print(f" Wait wake at idx={idx} host_ns={host_ns} ({host_ns/1e9:.3f}s) kind={kind}")
|
|
print(f" payload={json.dumps(payload)[:300]}")
|
|
join_wait_end_ns = host_ns
|
|
join_wait_end_eidx = i
|
|
wait_duration_ns = host_ns - join_wait_start_ns
|
|
print(f" DURATION: {wait_duration_ns/1e9:.3f} s")
|
|
break
|
|
|
|
# Save the full timeline window from join-wait spawn (anchor) through end.
|
|
with open(os.path.join(OUTDIR, "canary-tid6-from-anchor.csv"), "w") as f:
|
|
f.write("host_ns,tid_event_idx,kind,name,detail\n")
|
|
for host_ns, idx, kind, payload in all_evts[anchor_idx:]:
|
|
name = payload.get("name", "")
|
|
detail = json.dumps(payload)[:400].replace('"', '""')
|
|
f.write(f'{host_ns},{idx},{kind},{name},"{detail}"\n')
|
|
|
|
print(f"\nWrote canary-tid6-from-anchor.csv with {len(all_evts) - anchor_idx} events.")
|