tools(gate): -ExeOverride and -DebuggerScript for the offline pixel gate; #422 characterised over 40 instrumented runs (Campaign VM VM7)

-ExeOverride runs another AcDream.App.exe through the SAME isolated
settings/scene/capture path so two binaries compare without mixing tool
versions (VM0's recorded fieldOfView trap). -DebuggerScript launches the
client under cdb so ntdll's debug heap validates every free and a heap
corruption breaks at the corrupting site with a stack.

#422 (1-in-8 STATUS_HEAP_CORRUPTION exit after a pack-on offline capture):
16 runs attached + 24 runs under the debug heap on 1f151242, all clean
(P ~ 0.5 % at the filed rate). Recorded as not reproducible on the current
binary, carried as a watch item for the owner's VM7 matrix with the loop as
the first step if it recurs; no longer a merge blocker. Evidence under
docs/research/evidence/vm7 alongside the final pack-off-vs-base+normals
robust diff (5-10 px, lifestone band only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-23 04:32:27 +02:00
parent 6ac3cc1070
commit c69029bf58
7 changed files with 131 additions and 4 deletions

View file

@ -0,0 +1,11 @@
.sympath C:\Windows\System32
.symopt+ 0x40
sxe -c "!analyze -v; ~*kb 40; lm; q" bpe
sxe -c "!analyze -v; ~*kb 40; lm; q" av
sxe -c "!analyze -v; ~*kb 40; lm; q" c0000374
sxe -c "!analyze -v; ~*kb 40; lm; q" c0000409
sxd ld
sxd ud
sxd clr
sxd clrn
g

View file

@ -0,0 +1,39 @@
<#
.SYNOPSIS
#422 characterisation loop: N offline High captures, each launched under
cdb with the debug heap (tools/run-offline-pixel-gate.ps1 -DebuggerScript),
summarised to artifacts/i422dh/summary.txt.
.EXAMPLE
pwsh tools/i422/loop-debugheap.ps1 -Runs 24
#>
param([int]$Runs = 24)
$ErrorActionPreference = 'Continue'
$repo = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$root = Join-Path $repo 'artifacts\i422dh'
New-Item -ItemType Directory -Force $root | Out-Null
$summary = Join-Path $root 'summary.txt'
"run,gateExit,shutdownLine,heapLine" | Set-Content $summary
for ($i = 1; $i -le $Runs; $i++) {
$out = Join-Path $root ("run-{0:D2}" -f $i)
$gateOut = Join-Path $root ("gate-{0:D2}.out" -f $i)
$gateErr = Join-Path $root ("gate-{0:D2}.err" -f $i)
$gate = Start-Process -FilePath 'pwsh' -ArgumentList @(
'-NoProfile', '-File', (Join-Path $repo 'tools\run-offline-pixel-gate.ps1'),
'-Out', $out, '-SkipBuild', '-RenderPackPreset', 'high', '-Uncapped',
'-DebuggerScript', (Join-Path $repo 'toolsbi422-heap-launch.cdb')) `
-PassThru -WindowStyle Hidden -RedirectStandardOutput $gateOut -RedirectStandardError $gateErr
$gate.WaitForExit()
$clientLog = Join-Path $out 'client.log'
$shutdown = ''
$m = Select-String -Path $gateErr -Pattern 'exited with code|timed out|No screenshot' -ErrorAction SilentlyContinue | Select-Object -First 1
if ($m) { $shutdown = $m.Line.Trim() -replace ',', ';' }
$heap = ''
if (Test-Path $clientLog) {
$h = Select-String -Path $clientLog -Pattern 'HEAP\[|Critical error|heap corruption|FAULTING_IP|Access violation|STATUS_HEAP|second chance|!analyze' |
Where-Object { $_.Line -notmatch '^\d+:\d+> sxe' } | Select-Object -First 1
if ($h) { $heap = $h.Line.Trim() -replace ',', ';' }
}
"$i,$($gate.ExitCode),$shutdown,$heap" | Add-Content $summary
Write-Host ("run {0}: gate {1} {2} {3}" -f $i, $gate.ExitCode, $shutdown, $heap)
}
Get-Content $summary

View file

@ -125,6 +125,22 @@
.PARAMETER SkipBuild
Skip the Release build (use when the caller already built).
.PARAMETER ExeOverride
Run this AcDream.App.exe instead of the repository's Release build, with
the SAME isolated settings, scene and capture path. Campaign VM VM7 used
it to compare the final binary against a reference binary built in a
throwaway worktree (VM0's base+normals) without mixing tool versions
VM0 recorded that captures from two different tool versions are not
comparable (isolated-settings fieldOfView). Implies -SkipBuild.
.PARAMETER DebuggerScript
Launch the client UNDER cdb (x64 Windows Debugger, -G -o -lines -cf
<script>) instead of directly. A process started under a debugger runs
with ntdll's debug heap (per-free validation), so a heap corruption
breaks at the corrupting site with a stack instead of fail-fasting at
exit. Campaign VM VM7 used it to characterise #422. The debugger's
console output (which includes the client's stdout) goes to client.log.
.EXAMPLE
# Capture a baseline at the parent commit, then gate the slice:
git stash
@ -164,7 +180,9 @@ param(
# (#226). Default keeps the product default (on). Pass $false to capture the
# pack-off frame without the building/EnvCell detail replay.
[bool]$BuildingDetailTextures = $true,
[switch]$SkipBuild
[switch]$SkipBuild,
[string]$ExeOverride,
[string]$DebuggerScript
)
$ErrorActionPreference = 'Stop'
@ -173,6 +191,15 @@ $exe = Join-Path $repo 'src\AcDream.App\bin\Release\net10.0\AcDream.App.exe'
$cli = Join-Path $repo 'src\AcDream.Cli\bin\Release\net10.0\AcDream.Cli.dll'
. (Join-Path $PSScriptRoot 'atmospheric-performance-matrix-common.ps1')
if ($ExeOverride) {
if (-not (Test-Path $ExeOverride)) { throw "ExeOverride not found: $ExeOverride" }
$exe = (Resolve-Path $ExeOverride).Path
$SkipBuild = $true
}
if ($DebuggerScript -and -not (Test-Path $DebuggerScript)) {
throw "DebuggerScript not found: $DebuggerScript"
}
if ($AllowSafeRenderPackFallback -and $RenderPackPreset -eq 'retail') {
throw '-AllowSafeRenderPackFallback is valid only for an explicitly selected enhanced preset.'
}
@ -309,8 +336,16 @@ if ($MsaaSamples -ge 0) { $env:ACDREAM_MSAA_SAMPLES = "$MsaaSamples" }
else { Remove-Item Env:\ACDREAM_MSAA_SAMPLES -ErrorAction SilentlyContinue }
Write-Step "launching offline client (pack $packId/$presetId, $Resolution, $(if ($Uncapped) { 'uncapped' } else { 'capped' }), orbit ${OrbitDistanceMeters}m/$OrbitYawDegrees deg yaw/$OrbitPitchDegrees deg pitch, warmup ${WarmupMs}ms, day group $DayGroup, day fraction $WorldDayFraction, sky phase $SkyPhaseSeconds, MSAA $MsaaSamples)"
$proc = Start-Process -FilePath $exe -RedirectStandardOutput $log `
-RedirectStandardError "$log.err" -PassThru -WindowStyle Hidden
$proc = if ($DebuggerScript) {
$cdb = 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe'
if (-not (Test-Path $cdb)) { throw "cdb.exe not found at $cdb (install the Windows Debugging Tools)." }
Start-Process -FilePath $cdb `
-ArgumentList @('-G', '-o', '-lines', '-cf', (Resolve-Path $DebuggerScript).Path, $exe) `
-RedirectStandardOutput $log -RedirectStandardError "$log.err" -PassThru -WindowStyle Hidden
} else {
Start-Process -FilePath $exe -RedirectStandardOutput $log `
-RedirectStandardError "$log.err" -PassThru -WindowStyle Hidden
}
try {
$shots = Join-Path $Out 'screenshots'