From 8438f2e5b687900f60b9eac22ab0e61617cd7b7c Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Tue, 25 Aug 2026 15:38:23 +0000 Subject: [PATCH] re: counter-example weakens the probe-causes-freeze correlation A pilot-only run froze at t~150.7s (frozen.py: max_pixel_delta=0), found by accident when the sweep-free experiment aborted at startup and the run flew with no script probe attached. The tally is now 3-of-3 frozen with the probe versus 1-of-3 without, not 3-versus-0. Still a lean, but not the clean separation the previous entry claimed, and marked down accordingly -- the fourth time a freeze conclusion here has had to be softened by one more run. The sweep-free test itself could not run: ScriptMission is re-allocated per run, so the address from an earlier run (0xBC7A2A20) read back all zeros. The cheap self-consistency check ([m+44] must equal the phase's [+244]) rejected it instead of reporting garbage, which is the part that worked. Names the cheaper replacement: a BOUNDED pointer scan. Every ScriptMission seen so far sits in 0xBC79xxxx-0xBC7Axxxx, so ~32MB instead of ~371MB would cut the sweep cost roughly tenfold. Not yet implemented. --- docs/re/mission-freeze-resume-spin.md | 38 +++++++++++++++++++++++++++ tools/re-capture/phase_watch.py | 37 ++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/docs/re/mission-freeze-resume-spin.md b/docs/re/mission-freeze-resume-spin.md index 647ed018..c07ea234 100644 --- a/docs/re/mission-freeze-resume-spin.md +++ b/docs/re/mission-freeze-resume-spin.md @@ -1217,3 +1217,41 @@ cause; if they continue, the per-sample reads are. **Cost so far:** three attempts to observe a phase advance, all truncated inside four minutes by a freeze the probe itself may be causing. The arrival transition at ~143 s was caught only because it happens early. + +## 🟡 2026-08-25 — a counter-example weakens the probe correlation + +The previous entry made the probe the leading suspect on a clean 3-versus-2 +split. **A pilot-only run has now frozen too**, and it came from an accident: the +sweep-free experiment aborted at startup (below), so the run flew with *no* +script probe attached at all. Its pilot state went identical at **t ≈ 150.7 s** +and `frozen.py` confirms `max_pixel_delta=0`. + +| probe attached | clean | frozen | +|---|---|---| +| pilot only | 936 s, 1064 s | **150 s** | +| pilot + `phase_watch.py` | — | 70 s, 126 s, 253 s | + +So it is **3-of-3 with the probe versus 1-of-3 without**, not 3-versus-0. That is +still a lean, but it is no longer the clean separation the last entry described, +and I am marking it down rather than keeping the stronger reading. This is the +fourth time in this investigation a freeze conclusion has had to be softened by +one more run. + +**What would actually settle it** is unchanged and now clearly worth the cost: +alternate probe-on and probe-off windows *within* a single run, several runs, and +compare freeze rate per unit of mission time. Between-run comparisons at n≈3 +keep producing splits that the next run erodes. + +## 🔴 The sweep-free test could not run: the ScriptMission moves + +The plan was to skip `find_mission()`'s two memory sweeps by passing an address +from an earlier run. It does not survive: `0xBC7A2A20` read back **all zeros** +this run, and the cheap self-consistency check (`[m+44]` must equal the phase's +`[+244]`) **rejected it** rather than reporting garbage — which is the one thing +that went right here. + +So the object is re-allocated per run and a hard-coded address is not viable. +The sweeps still need replacing to run the experiment; the cheaper version is a +**bounded** pointer scan — every ScriptMission seen so far sat in +`0xBC79xxxx–0xBC7Axxxx`, so scanning ~32 MB instead of the full ~371 MB would cut +the cost roughly tenfold while still finding it. Not yet implemented. diff --git a/tools/re-capture/phase_watch.py b/tools/re-capture/phase_watch.py index d9c83467..d0c4bd84 100644 --- a/tools/re-capture/phase_watch.py +++ b/tools/re-capture/phase_watch.py @@ -14,7 +14,14 @@ It also does two things the earlier probes had to learn the hard way: * re-locates the ScriptMission if the pointer stops validating, rather than silently reporting stale numbers. -Usage: phase_watch.py [secs] [every_s] +`--mission 0x...` skips the two full guest-memory sweeps `find_mission()` does +and validates the given address with a handful of reads instead. That exists +because the sweeps are the leading suspect for the in-mission freeze: runs with +this probe attached froze at ~70/126/253 s, while the same runs without it went +936 s and 1064 s clean (mission-freeze-resume-spin.md). Sweep-free mode is the +experiment that separates "the sweeps do it" from "the per-sample reads do it". + +Usage: phase_watch.py [secs] [every_s] [--mission 0xADDR] """ import os import sys @@ -33,15 +40,35 @@ def main(): sym2 = isl.symbols(ssb, 2) secs = float(sys.argv[2]) if len(sys.argv) > 2 else 900 every = float(sys.argv[3]) if len(sys.argv) > 3 else 5 + given = None + if '--mission' in sys.argv: + given = int(sys.argv[sys.argv.index('--mission') + 1], 0) path = gmem.mem_path() size = os.path.getsize(path) import frozen t0 = time.time() with open(path, 'rb', buffering=0) as f: - m, fb = S.find_mission(f, size, ssb) - if m is None: - print('ScriptMission not located'); return 1 - print('file base 0x%08X ScriptMission 0x%08X' % (fb, m), flush=True) + if given is not None: + # Validate WITHOUT sweeping: the mission's symtab1 pointer must equal + # the phase's, and the code base must be a plausible pointer. Both + # are self-consistency checks internal to the two objects, so they + # cost four reads instead of two full scans. + code = S.u32(f, given + 24) + s44 = S.u32(f, given + 44) + ph = S.u32(f, given + 4) + p244 = S.u32(f, ph + 244) if ph else None + ok = (code and 0x10000 <= code < 0xFFFFFFF0 and s44 and s44 == p244) + if not ok: + print('given ScriptMission 0x%08X does not validate ' + '(code=%s s44=%s phase244=%s)' % (given, code, s44, p244)) + return 1 + m, fb = given, None + print('ScriptMission 0x%08X (given, validated, NO sweeps)' % m, flush=True) + else: + m, fb = S.find_mission(f, size, ssb) + if m is None: + print('ScriptMission not located'); return 1 + print('file base 0x%08X ScriptMission 0x%08X' % (fb, m), flush=True) last = None next_frozen = 0.0 while time.time() - t0 < secs: