param( [Parameter(Mandatory)][string]$OutputDirectory, [ValidateRange(4,8)][int]$Pairs = 4, [string]$PythonExecutable, [ValidateRange(0,9223372036854775807)][long]$ProbeAffinityMask = 0, [switch]$AnalyzeOnly ) # One controlled batch of OFF/WPR-ON fixed-work synthetic runs; no Wii/NAND. # WPR requires an elevated PowerShell. Every run gets a unique directory. $ErrorActionPreference = 'Stop' $p1Repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $p1Root = [IO.Path]::GetFullPath($(if ([IO.Path]::IsPathRooted($OutputDirectory)) { $OutputDirectory } else { Join-Path $p1Repo $OutputDirectory })) if ($PythonExecutable) { $p1Python = (Resolve-Path -LiteralPath $PythonExecutable).Path } else { $p1Candidates = @(Get-Command python.exe -All -ErrorAction SilentlyContinue | Where-Object { $_.Source -notlike '*\WindowsApps\*' } | Select-Object -ExpandProperty Source) $p1Candidates += 'C:\msys64\mingw64\bin\python.exe' foreach ($p1Candidate in $p1Candidates) { if ($p1Candidate -and (Test-Path -LiteralPath $p1Candidate)) { $p1Python = $p1Candidate break } } } if (-not $p1Python) { throw 'Python was not found before capture. Pass -PythonExecutable with the path to a working python.exe.' } & $p1Python --version | Out-Null if ($LASTEXITCODE) { throw "Python could not start: $p1Python" } $p1AlreadyExists = Test-Path -LiteralPath $p1Root if ($AnalyzeOnly -and -not $p1AlreadyExists) { throw "Existing batch not found: $p1Root" } if (-not $AnalyzeOnly -and $p1AlreadyExists) { throw 'Use a NEW batch directory for capture. Pass -AnalyzeOnly to compare existing captures.' } $p1Probe = Join-Path $p1Repo '.starlet_diag_p1/Binaries/Tests/TraceProbeP1.exe' $p1Network = Join-Path $p1Repo '.starlet_diag_p1/Binaries/Network' $p1Decoder = Join-Path $p1Repo '.starlet-profile/P1-tools/DecodeP1.exe' if (-not $AnalyzeOnly) { foreach ($p1Required in @($p1Probe, $p1Network, $p1Decoder)) { if (-not (Test-Path -LiteralPath $p1Required)) { throw "Missing dependency: $p1Required" } } $p1Drive = [IO.DriveInfo]::new([IO.Path]::GetPathRoot($p1Root)) if ($p1Drive.AvailableFreeSpace -lt 9GB) { throw 'Need 4 GiB batch budget plus 5 GiB reserve.' } New-Item -ItemType Directory -Path $p1Root | Out-Null } $p1Runs = @() for ($p1Index = 1; $p1Index -le $Pairs; ++$p1Index) { $p1Off = Join-Path $p1Root "off-$p1Index" $p1On = Join-Path $p1Root "wpr-on-$p1Index" if (-not $AnalyzeOnly) { Write-Output "Pair $p1Index/${Pairs}: fixed-work OFF" & (Join-Path $PSScriptRoot 'Capture-Private-P1.ps1') -OutputDirectory $p1Off -NoTrace ` -ProbeAffinityMask $ProbeAffinityMask Write-Output "Pair $p1Index/${Pairs}: fixed-work WPR ON" & (Join-Path $PSScriptRoot 'Capture-P1.ps1') -OutputDirectory $p1On -Seconds 60 ` -ProbeExecutable $p1Probe ` -ProbeArguments @((Join-Path $p1On 'synthetic.journal'), $p1Network) ` -ProbeAffinityMask $ProbeAffinityMask $p1Decoded = & $p1Decoder (Join-Path $p1On 'capture.etl') ` (Join-Path $p1On 'native-events.jsonl') $p1DecodeExit = $LASTEXITCODE $p1Decoded | Out-File -LiteralPath (Join-Path $p1On 'decoder-summary.json') -Encoding utf8 if ($p1DecodeExit) { throw "Pair $p1Index has ETW loss or native decode errors." } } $p1Runs += $p1Off, $p1On } $p1Comparison = Join-Path $p1Root 'comparison.json' if (Test-Path -LiteralPath $p1Comparison) { throw "Comparison already exists and will not be overwritten: $p1Comparison" } & $p1Python (Join-Path $PSScriptRoot 'compare_p1.py') @p1Runs --output $p1Comparison if ($LASTEXITCODE) { throw 'Fixed-work comparison failed; no Wii run was started.' } Write-Output "Overhead batch finished: $p1Comparison"