param( [Parameter(Mandatory = $true)][int]$AppPid, [Parameter(Mandatory = $true)][string]$OutputPath, [int]$Seconds = 90, [int]$IntervalSeconds = 2, [int]$AbortAtWorkingSetMB = 0, [string]$ExpectedExecutable = '' ) # Independent telemetry for native-webview-stress.mjs. Follow only descendants of the # isolated app PID; a hung shell CDP target cannot stop this watcher from recording CPU. $previousCpu = @{} $watch = [System.Diagnostics.Stopwatch]::StartNew() while ($watch.Elapsed.TotalSeconds -lt $Seconds) { $all = @(Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId) $ids = @($AppPid) do { $children = @($all | Where-Object { $ids -contains $_.ParentProcessId -and $ids -notcontains $_.ProcessId } | Select-Object -ExpandProperty ProcessId) $ids += $children } while ($children.Count -gt 0) $processes = @(Get-Process -Id $ids -ErrorAction SilentlyContinue) if (-not ($processes | Where-Object Id -EQ $AppPid)) { break } $rows = @($processes | ForEach-Object { $cpu = if ($null -eq $_.CPU) { 0 } else { [double]$_.CPU } $before = if ($previousCpu.ContainsKey($_.Id)) { $previousCpu[$_.Id] } else { $cpu } $previousCpu[$_.Id] = $cpu [pscustomobject]@{ pid = $_.Id name = $_.ProcessName cpuDeltaSeconds = [math]::Round($cpu - $before, 3) cpuTotalSeconds = [math]::Round($cpu, 3) workingSetMB = [math]::Round($_.WorkingSet64 / 1MB) privateMB = [math]::Round($_.PrivateMemorySize64 / 1MB) } } | Sort-Object cpuDeltaSeconds -Descending) $totalWorkingSetMB = ($rows | Measure-Object workingSetMB -Sum).Sum [pscustomobject]@{ elapsedSeconds = [math]::Round($watch.Elapsed.TotalSeconds, 2) processCount = $rows.Count totalCpuDeltaSeconds = [math]::Round(($rows | Measure-Object cpuDeltaSeconds -Sum).Sum, 3) totalWorkingSetMB = $totalWorkingSetMB top = @($rows | Select-Object -First 8) } | ConvertTo-Json -Depth 5 -Compress | Add-Content -LiteralPath $OutputPath if ($AbortAtWorkingSetMB -gt 0 -and $totalWorkingSetMB -ge $AbortAtWorkingSetMB) { $app = Get-CimInstance Win32_Process -Filter "ProcessId=$AppPid" if ($app -and $ExpectedExecutable -and $app.ExecutablePath -ieq $ExpectedExecutable) { [pscustomobject]@{ event = 'memory-abort'; appPid = $AppPid; workingSetMB = $totalWorkingSetMB } | ConvertTo-Json -Compress | Add-Content -LiteralPath $OutputPath Stop-Process -Id $AppPid -Force } break } Start-Sleep -Seconds $IntervalSeconds }