Files
xenia-rs/audit-runs/review-a-step2-natural-trigger/find_signaler.py
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

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")