BT410 5.3.56: the intermittent pod crash is the skeleton walk's -- attributed by same-binary A/B

Added BT_NO_SKL, which skips the skeleton build so ONE executable can be run
with and without it.  That is the only clean way to test an intermittent
fault, and it settles attribution:

  walk enabled   ~50% of runs die (page fault at 66D9) at DIFFERENT points
  walk disabled  0 crashes in 2 runs
  shipped exe    0 crashes in 2 runs, same rig and conf

So it is our new code, not the rig, and the varying fault point points at
memory corruption surfacing later rather than a bad instruction in the walk.

Hypotheses killed by reading the private library headers: the matrix array is
the right size (dpl_MATRIX is float32[4][4]); and neither s_dplobject nor the
common dpl_node header retains an object name, weakening the dangling-string
theory (a private name cache inside the library is still possible and is the
best surviving suspect).

Also checked something that would have been much worse than a crash:
~NotationFile REWRITES its file when dirty, and these are the game's shipped
.SKL data files.  MAD.SKL is untouched, and ReadSKLFile now carries the
engine's own Verify(!IsDirty()) guard.

Next tests are listed cheapest-first in the roadmap: keep the NotationFile
alive, copy the Object= names, count board allocations, and re-check whether
MakeEntityRenderables runs twice per mech.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-28 20:45:28 -05:00
co-authored by Claude Fable 5
parent d66c2b0812
commit f54e1a3c19
5 changed files with 136 additions and 3 deletions
+16
View File
@@ -159,6 +159,12 @@ dpl_DCS *
<< " -> " << node_count << " nodes, "
<< object_count << " objects\n" << flush;
//
// The engine guards this the same way (L4VIDEO.CPP, DPLReadEnvironment):
// ~NotationFile REWRITES the file when dirtyFlag is set, and these are
// the game's shipped .SKL data files.
//
Verify(!skeleton->IsDirty());
Unregister_Object(skeleton);
delete skeleton;
return root;
@@ -388,6 +394,16 @@ void
Logical
handled = False;
//
// BT_NO_SKL skips the skeleton build so the SAME binary can be
// run with and without it -- the only way to attribute the
// intermittent pod crash without a rebuild between samples.
//
if (getenv("BT_NO_SKL") != NULL)
{
break;
}
Entity_Being_Created = entity;
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
@@ -776,3 +776,49 @@ That defect has therefore gone from background annoyance to the top of the
list: it is what stops a mech frame from ever being seen, because a run has
to survive BOTH the skeleton build AND the launch to render anything, and at
~50% per run that is a coin flip on a four-minute cycle.
--------------------------------------------------------------------------------
THE INTERMITTENT POD CRASH IS THE SKELETON WALK'S -- attributed, not yet fixed
--------------------------------------------------------------------------------
Attributed with a same-binary A/B, which is the only clean way to test an
intermittent fault: BT_NO_SKL skips the skeleton build, so one executable can
be run both ways with nothing else differing.
walk ENABLED ~50% of runs die ("Reference to a page you don't own",
PF at 66D9), at DIFFERENT points each time -- sometimes
mid-walk, sometimes after it, sometimes before launch
walk DISABLED 0 crashes in 2 runs
SHIPPED exe 0 crashes in 2 runs on the same rig and conf
So it is our new code, it is not the rig, and the varying fault point says
memory corruption surfacing later rather than a bad instruction in the walk.
HYPOTHESES KILLED so far (all by reading the private library headers, so they
stay killed):
* matrix array too small -- dpl_MATRIX is float32[4][4] (DPL_PRIV.H:137),
exactly the 16 floats we pass.
* dangling Object= name. dpl_LoadObject is handed a pointer INTO the
NotationFile, which we then delete -- but neither s_dplobject
(DPL_PRIV.H:377) nor the common dpl_node header (DPL_PRIV.H:92) keeps a
name field, so the library does not retain it there. A private name
cache is still possible and remains the best surviving suspect.
* NotationFile rewriting the game's data. ~NotationFile DOES rewrite the
file when dirtyFlag is set, which would have corrupted the shipped .SKL
files -- checked, MAD.SKL is untouched (11847 bytes, Jan 2001), and the
engine's own Verify(!IsDirty()) guard is now copied into ReadSKLFile.
STILL TO TRY, cheapest first:
1. Keep the NotationFile alive for the mission instead of deleting it (a
~12KB leak) and see if the crash rate drops -- that settles the name
cache question outright.
2. Copy each Object= name into storage that outlives the walk, same test.
3. Count what the walk allocates on the board (26 DCS + 19 objects + 19
instances + 1 zone per mech) against whatever the VPX HLE's node tables
allow -- resource exhaustion would also present as a late, varying
fault.
4. Check whether MakeEntityRenderables is invoked more than once per mech
(inside AND outside views). The [skl] summary line printed once per run
here, so this is unlikely, but it is cheap to re-check with two mechs.
BT_NO_SKL stays in the tree: it is how any future "is it the skeleton?"
question gets answered in one build instead of two.