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>
136 lines
5.2 KiB
Python
136 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Find what signals canary tid=17's NtWaitForSingleObjectEx event.
|
|
|
|
The wait at idx=432-435 is for an event created by tid=17 itself.
|
|
Find handle_semantic_ids for events tid=17 creates, then find
|
|
wait.wake / NtSetEvent on those handles from OTHER tids.
|
|
"""
|
|
import json
|
|
import os
|
|
from collections import defaultdict, 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__))
|
|
|
|
# Look at window [1.9..2.1s] for all events.
|
|
T_LO = 1_900_000_000
|
|
T_HI = 2_100_000_000
|
|
|
|
# Handles created by tid=17:
|
|
tid17_creates = []
|
|
# wait.begin by tid=17:
|
|
tid17_waits = []
|
|
# NtSetEvent / NtReleaseSemaphore by ALL tids in window with handle ids:
|
|
signal_events = []
|
|
# wait.wake/end events with handle ids:
|
|
wake_events = []
|
|
|
|
# Also track all handle.create events in window.
|
|
all_handles = {}
|
|
|
|
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
|
|
try:
|
|
ev = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
kind = ev.get("kind", "")
|
|
tid = ev.get("tid")
|
|
payload = ev.get("payload", {})
|
|
if kind == "handle.create":
|
|
hsid = payload.get("handle_semantic_id")
|
|
rh = payload.get("raw_handle_id")
|
|
ot = payload.get("object_type")
|
|
all_handles[hsid] = {"raw_handle_id": rh, "object_type": ot, "creator_tid": tid, "host_ns": host_ns}
|
|
if tid == 17:
|
|
tid17_creates.append(ev)
|
|
elif kind == "wait.begin" and tid == 17:
|
|
tid17_waits.append(ev)
|
|
elif kind in ("wait.wake", "wait.end"):
|
|
wake_events.append(ev)
|
|
elif kind == "import.call":
|
|
n = payload.get("name", "")
|
|
if n in ("NtSetEvent", "NtReleaseSemaphore", "NtSetEventBoostPriority", "KeSetEvent"):
|
|
signal_events.append((host_ns, ev["tid_event_idx"], tid, n, payload))
|
|
|
|
print(f"tid=17 handle.create events: {len(tid17_creates)}")
|
|
print(f"tid=17 wait.begin events: {len(tid17_waits)}")
|
|
print(f"signal events (NtSetEvent/NtReleaseSemaphore/...): {len(signal_events)}")
|
|
print(f"wait.wake/end events: {len(wake_events)}")
|
|
|
|
# Show the wait.begin events on tid=17.
|
|
print("\n=== tid=17 wait.begin events ===")
|
|
for ev in tid17_waits[:30]:
|
|
pl = ev["payload"]
|
|
hids = pl.get("handles_semantic_ids", [])
|
|
timeout = pl.get("timeout_ns")
|
|
# Resolve handle:
|
|
info = [all_handles.get(h, {}) for h in hids]
|
|
print(f" t={ev['host_ns']/1e9:.5f}s idx={ev['tid_event_idx']} handles={hids} timeout={timeout}")
|
|
for h, inf in zip(hids, info):
|
|
print(f" {h} -> {inf}")
|
|
|
|
# Wake events for tid=17 in window.
|
|
print("\n=== wake events targeting tid=17 ===")
|
|
for ev in wake_events:
|
|
pl = ev["payload"]
|
|
if ev.get("tid") == 17:
|
|
print(f" t={ev['host_ns']/1e9:.5f}s idx={ev['tid_event_idx']} kind={ev['kind']} payload={json.dumps(pl)[:250]}")
|
|
|
|
# Now find signalers of tid=17's wait handles.
|
|
# Build set of hsid that tid=17 waited on.
|
|
wait_hsids = set()
|
|
for ev in tid17_waits:
|
|
for h in ev["payload"].get("handles_semantic_ids", []):
|
|
wait_hsids.add(h)
|
|
print(f"\n=== Unique handle semantic IDs waited on by tid=17: {len(wait_hsids)} ===")
|
|
for h in list(wait_hsids)[:20]:
|
|
info = all_handles.get(h, {})
|
|
print(f" {h} -> {info}")
|
|
|
|
# Check: for each, who created it? Object type? In this window's signal_events, who signals?
|
|
# The NtSetEvent / NtReleaseSemaphore events don't carry handle info in payload by default
|
|
# (payload is empty args:{}). Print first few to confirm.
|
|
print("\n=== Sample signal events (first 10) ===")
|
|
for s in signal_events[:10]:
|
|
print(f" t={s[0]/1e9:.5f}s idx={s[1]} tid={s[2]} name={s[3]} payload_keys={list(s[4].keys())}")
|
|
|
|
# Count signal events per tid in the window.
|
|
sig_counts = Counter()
|
|
for s in signal_events:
|
|
sig_counts[(s[2], s[3])] += 1
|
|
print(f"\n=== Signal event counts by (tid, name) in window [{T_LO/1e9}..{T_HI/1e9}s] ===")
|
|
for (tid, name), c in sorted(sig_counts.items()):
|
|
print(f" tid={tid:3d} {name:30s} {c}")
|
|
|
|
# Save tid=17 timeline showing wait.begin/wait.wake.
|
|
with open(os.path.join(OUTDIR, "canary-tid17-waits.csv"), "w") as f:
|
|
f.write("host_ns,tid_event_idx,kind,handles,timeout_ns,result\n")
|
|
for ev in tid17_waits:
|
|
pl = ev["payload"]
|
|
hids = ",".join(pl.get("handles_semantic_ids", []))
|
|
timeout = pl.get("timeout_ns", "")
|
|
f.write(f'{ev["host_ns"]},{ev["tid_event_idx"]},{ev["kind"]},"{hids}",{timeout},\n')
|
|
for ev in wake_events:
|
|
if ev.get("tid") != 17:
|
|
continue
|
|
pl = ev["payload"]
|
|
hids = ",".join(pl.get("handles_semantic_ids", []))
|
|
f.write(f'{ev["host_ns"]},{ev["tid_event_idx"]},{ev["kind"]},"{hids}","","{json.dumps(pl)[:200]}"\n')
|
|
|
|
print(f"\nWrote canary-tid17-waits.csv")
|