<# .SYNOPSIS Runs the SDK 1.5.378.182 comparator (lmxopcua#799) for 7,200 s on Windows with the same envelope as ../windows/launch.ps1: private task directory for TEMP and fixture PKI, ephemeral fixture credential, external 30 s sampler, 7,440 s outer deadline. #> param( [Parameter(Mandatory)] [string] $Bin, [Parameter(Mandatory)] [string] $Output, [Parameter(Mandatory)] [string] $SourceRevision ) $ErrorActionPreference = 'Stop' if (Test-Path $Output) { throw 'Use a fresh output directory' } New-Item -ItemType Directory -Path $Output | Out-Null $Output = (Resolve-Path $Output).Path New-Item -ItemType Directory -Path (Join-Path $Output 'tmp') | Out-Null & icacls $Output /inheritance:r /grant:r "$($env:USERNAME):(OI)(CI)F" '*S-1-5-18:(OI)(CI)F' | Out-Null $exe = Join-Path (Resolve-Path $Bin).Path 'sdk1-comparator.exe' $started = Get-Date # Monotonic: the 2026-11-12 windev run lost its last 186 s when a +552 s host clock step (w32time) advanced a wall-clock deadline. $clock = [System.Diagnostics.Stopwatch]::StartNew() $bytes = New-Object byte[] 3; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) $env:OTOPCUA_BASELINE_CANONICAL_KEY = 'otua_k-server-events_' + (($bytes | ForEach-Object { $_.ToString('x2') }) -join '') $env:OTOPCUA_COMPARATOR_SOURCE = $SourceRevision $env:TEMP = Join-Path $Output 'tmp'; $env:TMP = $env:TEMP @{ kind = 'comparator-launch'; sourceRevision = $SourceRevision; exe = $exe exeSha256 = (Get-FileHash -Algorithm SHA256 $exe).Hash.ToLowerInvariant() coreClrSha256 = (Get-FileHash -Algorithm SHA256 (Join-Path (Split-Path $exe) 'coreclr.dll')).Hash.ToLowerInvariant() startedUtc = $started.ToUniversalTime().ToString('o'); credentialBytes = $env:OTOPCUA_BASELINE_CANONICAL_KEY.Length } | ConvertTo-Json | Set-Content -Encoding UTF8 (Join-Path $Output 'launch.json') $psi = New-Object System.Diagnostics.ProcessStartInfo $exe $psi.Arguments = "`"$(Join-Path $Output 'run')`" 7200 --comparator" $psi.UseShellExecute = $false; $psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true $proc = [System.Diagnostics.Process]::Start($psi) $outTask = $proc.StandardOutput.ReadToEndAsync(); $errTask = $proc.StandardError.ReadToEndAsync() $sampler = Join-Path $Output 'external-samples.jsonl' while (-not $proc.WaitForExit(30000)) { $elapsed = $clock.Elapsed.TotalSeconds $row = [ordered]@{ utc = (Get-Date).ToUniversalTime().ToString('o'); launcherElapsedSeconds = [math]::Round($elapsed, 1) } try { $p = Get-Process -Id $proc.Id -ErrorAction Stop $row.workingSet64 = $p.WorkingSet64; $row.privateMemorySize64 = $p.PrivateMemorySize64 $row.peakWorkingSet64 = $p.PeakWorkingSet64; $row.handleCount = $p.HandleCount; $row.threads = $p.Threads.Count } catch { $row.targetGone = $true } ($row | ConvertTo-Json -Compress) | Add-Content -Encoding UTF8 $sampler if ($elapsed -gt 7440) { & taskkill /PID $proc.Id /T /F | Out-Null; 'outer-deadline' | Set-Content (Join-Path $Output 'launcher-outcome.txt') } } Set-Content -Encoding UTF8 (Join-Path $Output 'stdout.log') $outTask.Result Set-Content -Encoding UTF8 (Join-Path $Output 'stderr.log') $errTask.Result "$($proc.ExitCode)" | Set-Content -Encoding ASCII (Join-Path $Output 'exit-status.txt') 'done' | Add-Content (Join-Path $Output 'launcher-outcome.txt')