Private/Watchdog-MonitorInstaller.ps1
|
<#
SPDX-License-Identifier: MIT Copyright (c) 2026 Leon McClatchey, Linktech Engineering LLC Package: VSCode-Updater Author: Leon McClatchey Company: Linktech Engineering LLC Created: 2026-04-16 Modified: 2026-04-17 File: Private/Watchdog-MonitorInstaller.ps1 Version: 1.0.0 Description: Monitors the VS Code installer and related worker processes for CPU and disk activity, detects idle or stalled states, and terminates processes when the installer becomes unresponsive. #> function Watchdog-MonitorInstaller { param( $ChildProcess, [int]$ParentPID, [int]$IdleTimeout ) $idleSeconds = 0 $lastState = "" $lastCPU = 0 $lastDisk = 0 $fsIdleSeconds = 0 $activeSeconds = 0 $installPath = "$env:LOCALAPPDATA\Programs\Microsoft VS Code" $lastWriteTime = (Get-Date) $fsLogCooldown = 30 # seconds $lastFsLog = (Get-Date).AddSeconds(-10) Write-Log "[WATCHDOG] Monitoring child PID $($ChildProcess.Id), parent PID $ParentPID" while ($true) { Start-Sleep -Seconds 2 # Always re-query the child process — never trust the stale snapshot $child = Get-Process -Id $ChildProcess.Id -ErrorAction SilentlyContinue if (-not $child) { Write-Log "[WATCHDOG] Child exited — success" return "Success" } # Increment FS idle timer every loop $fsIdleSeconds += 2 # Detect file system activity in the VS Code directory (exclude logs/temp) try { $latestWrite = Get-ChildItem -Recurse $installPath -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension -notin '.log', '.tmp', '.bak' -and $_.FullName -notmatch '\\logs?\\' -and $_.FullName -notmatch '\\Crashpad\\' -and $_.FullName -notmatch '\\User Data\\' -and $_.FullName -notmatch '\\WebView2\\' } | Sort-Object LastWriteTime | Select-Object -Last 1 if ($latestWrite -and $latestWrite.LastWriteTime -gt $lastWriteTime) { if ((Get-Date) -gt $lastFsLog.AddSeconds($fsLogCooldown)) { Write-Log "[WATCHDOG] FS activity (real installer file): $($latestWrite.Name)" $lastFsLog = Get-Date } $lastWriteTime = $latestWrite.LastWriteTime $fsIdleSeconds = 0 $activeSeconds = 0 $idleSeconds = 0 } } catch { # Directory may not exist yet — ignore } # Filesystem stall detection if ($fsIdleSeconds -ge $IdleTimeout) { Write-Log "[WATCHDOG] No filesystem activity for $IdleTimeout seconds — killing installer" Stop-Process -Id $ChildProcess.Id -Force -ErrorAction SilentlyContinue Stop-Process -Id $ParentPID -Force -ErrorAction SilentlyContinue return "FS-Stalled" } $cpu = $child.CPU $disk = $child.IOReadBytes + $child.IOWriteBytes if ($cpu -eq 0 -and $disk -eq 0) { $idleSeconds += 2 if ($lastState -ne "Idle") { Write-Log "[WATCHDOG] Child transitioned to idle" $lastState = "Idle" } Write-Log "[WATCHDOG] Idle for $idleSeconds seconds" if ($idleSeconds -ge $IdleTimeout) { Write-Log "[WATCHDOG] Idle threshold reached — killing parent PID $ParentPID" Stop-Process -Id $ParentPID -Force -ErrorAction SilentlyContinue Write-Log "[WATCHDOG] Waiting for child PID $($ChildProcess.Id) to exit" Wait-Process -Id $ChildProcess.Id -ErrorAction SilentlyContinue return "Idle-Stalled" } } else { # Detect transition to active if ($lastState -ne "Active") { Write-Log "[WATCHDOG] Child transitioned to active" $lastState = "Active" } # Detect stalled active state if ($cpu -eq $lastCPU -and $disk -eq $lastDisk) { $activeSeconds += 2 if ($activeSeconds -ge $IdleTimeout) { Write-Log "[WATCHDOG] Child is stalled in active state — killing parent PID $ParentPID and child PID $($ChildProcess.Id)" Stop-Process -Id $ChildProcess.Id -Force -ErrorAction SilentlyContinue Stop-Process -Id $ParentPID -Force -ErrorAction SilentlyContinue try { Wait-Process -Id $ChildProcess.Id -Timeout 10 -ErrorAction SilentlyContinue } catch { Write-Log "[WATCHDOG] Child did not exit within timeout — continuing anyway" } return "Active-Stalled" } } else { # Progress is being made $activeSeconds = 0 } # Update last metrics $lastCPU = $cpu $lastDisk = $disk # Reset idle counter $idleSeconds = 0 } } } |