param( [Parameter(Mandatory=$true)][ValidateSet('P1-trace-005','P1-trace-006')][string]$Name, [Parameter(Mandatory=$true)][int]$DolphinProcessId, [ValidateRange(2,60)][int]$IntervalSeconds=10, [ValidateRange(1,120)][int]$MaxMinutes=60 ) # Low-overhead, read-only host progress samples for the paired native update. # No debugger, ETW session, guest-memory reads, or emulator input is involved. $ErrorActionPreference = 'Stop' $p1Repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $p1Root = Join-Path $p1Repo '.starlet_update_ab_20260913' $p1Profile = Join-Path $p1Root $Name $p1ExpectedExe = Join-Path $p1Root "build-$Name/Binaries/DolphinNoGUI.exe" $p1Journal = Join-Path $p1Profile 'Starlet/0238188DE97894AE3AF93A92C4F238C0572731A7.nand-journal' $p1Process = Get-Process -Id $DolphinProcessId -ErrorAction Stop if ($p1Process.ProcessName -ne 'DolphinNoGUI') { throw "Process $DolphinProcessId is not DolphinNoGUI." } # The restricted sampler cannot always read another process's executable path. # Check it when available, and require the isolated journal to be live as well. if ($p1Process.Path -and -not $p1Process.Path.Equals($p1ExpectedExe, [StringComparison]::OrdinalIgnoreCase)) { throw "Process $DolphinProcessId is not the frozen build for $Name." } if (-not (Test-Path -LiteralPath $p1Journal -PathType Leaf)) { throw "Missing isolated NAND journal: $p1Journal" } $p1Started = $p1Process.StartTime if ((Get-Item -LiteralPath $p1Journal).LastWriteTime -lt $p1Started) { throw "The isolated journal has not been written by the current launch." } $p1Capture = Join-Path $p1Profile ('Capture/host-progress-' + (Get-Date -Format 'yyyyMMdd-HHmmss') + '.jsonl') $p1Stream = [IO.File]::Open($p1Capture, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::Read) $p1Writer = [IO.StreamWriter]::new($p1Stream, [Text.UTF8Encoding]::new($false)) $p1Writer.AutoFlush = $true $p1Clock = [Diagnostics.Stopwatch]::StartNew() Write-Output "Capturing $Name (PID $DolphinProcessId) to $p1Capture" try { while ($p1Clock.Elapsed.TotalMinutes -lt $MaxMinutes) { $p1Process = Get-Process -Id $DolphinProcessId -ErrorAction SilentlyContinue if (-not $p1Process -or $p1Process.StartTime -ne $p1Started) { break } $p1Process.Refresh() [ordered]@{ UTC=[datetime]::UtcNow.ToString('o') ElapsedSeconds=[math]::Round($p1Clock.Elapsed.TotalSeconds,3) ProcessCPUSeconds=[math]::Round($p1Process.TotalProcessorTime.TotalSeconds,3) JournalBytes=(Get-Item -LiteralPath $p1Journal).Length PrivateMemoryBytes=$p1Process.PrivateMemorySize64 } | ConvertTo-Json -Compress | ForEach-Object { $p1Writer.WriteLine($_) } Start-Sleep -Seconds $IntervalSeconds } $p1Writer.WriteLine(('CAPTURE_END_UTC=' + [datetime]::UtcNow.ToString('o'))) } finally { $p1Writer.Dispose() } Write-Output "Saved $p1Capture"