Last active
January 10, 2025 03:32
-
-
Save Frosthaven/5b9164751d298615797683f9d62cf610 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ABOUT ------------------------------------------------------------------------ | |
| # ------------------------------------------------------------------------------ | |
| # AUTHOR: Frosthaven | |
| # DISCORD: frosthaven | |
| # DESCRIPTION: This is a powershell script that accomplishes the following: | |
| # 1. Launches a process | |
| # 2. Sets the affinity of the process to include or exclude specific cpu cores | |
| # 3. Optionally monitors the process for hangs and kills it if it hangs | |
| # 4. Optionally restarts the process if it hangs | |
| # USAGE ------------------------------------------------------------------------ | |
| # ------------------------------------------------------------------------------ | |
| # 1. Configure the script via the CONFIG section below. | |
| # 2a. Launch via right click: | |
| # - Right click the script and choosing "Run with PowerShell" | |
| # 2b. Launch via customizable shortcut: | |
| # - Right click the desktop | |
| # - Choose New > Shortcut | |
| # - Enter the following command into the target field (edit path as needed): | |
| # powershell.exe -command "& 'C:\A path with spaces\MyScript.ps1'" | |
| # Note: You can run this script after the process is already running if you | |
| # need to. It will attach to the already running process and set the affinity. | |
| # CONFIG ----------------------------------------------------------------------- | |
| # ------------------------------------------------------------------------------ | |
| # Process Launch ----------------------- | |
| # The $exePath is the path to the executable you want to launch. The | |
| # $processNameInTaskManager value is the name of the process as it appears in | |
| # the task manager. The $sleepTime is the number of seconds to wait after | |
| # launching the process before giving up on finding it in the task manager. | |
| $exePath = "C:\Program Files (x86)\Steam\steamapps\common\Path of Exile 2\PathOfExileSteam.exe" | |
| $processNameInTaskManager = "PathOfExileSteam" | |
| $sleepTime = 4 | |
| # Core Affinity ------------------------ | |
| # I'm including two ways to do this below. The first way will include all | |
| # available cores except the first two by starting the array after core 0 and 1. | |
| # The second way will give more explicit control over EXACTLY which cores you | |
| # want to have active. You can change which you want to enable/disable by | |
| # changing which $enabledCores is commented out with a "#" character. | |
| $coreCount = [Environment]::ProcessorCount | |
| $enabledCores = 2..($coreCount - 1) | |
| # $enabledCores = @(2, 3, 4) | |
| # CPU Usage Auto-Kill ------------------ | |
| # Here you can set the script to automatically kill the process if it hangs, and | |
| # optionally restart it. The $processHangTimeout is the number of seconds the | |
| # script will wait for the process to respond before killing it. | |
| $autoKillOnProcessHang = $true | |
| $autoRestart = $true | |
| $processHangTimeout = 5 | |
| # RUN SCRIPT ------------------------------------------------------------------- | |
| # ------------------------------------------------------------------------------ | |
| $host.privatedata.ProgressForegroundColor = "Green" | |
| $host.privatedata.ProgressBackgroundColor = "Black" | |
| # Start Process ------------------------ | |
| Clear-Host | |
| $runningProcess = Get-Process -Name $processNameInTaskManager -ErrorAction SilentlyContinue | |
| if ($runningProcess) { | |
| Write-Host "Process running..." | |
| } else { | |
| Write-Host "Launching process..." | |
| Start-Process $exePath -PassThru > $null | |
| Start-Sleep -Seconds $sleepTime > $null | |
| } | |
| Write-Host "" | |
| Write-Host "" | |
| Write-Host "" | |
| Write-Host "" | |
| Write-Host "" | |
| Write-Host "" | |
| # Wait for task manager entry ---------- | |
| $timeout = 10 | |
| $elapsedTime = 0 | |
| $activePID = $null | |
| while ($null -eq $activePID -and $elapsedTime -lt $timeout) { | |
| $runningProcess = Get-Process -Name $processNameInTaskManager -ErrorAction SilentlyContinue | |
| if ($runningProcess) { | |
| $activePID = $runningProcess.Id | |
| } else { | |
| Start-Sleep -Seconds 1 | |
| $elapsedTime++ | |
| } | |
| } | |
| if ($null -ne $activePID) { | |
| # Set affinity --------------------- | |
| Write-Host "" | |
| Write-Host "Configuring affinity bitmask..." | |
| $bitmask = 0 | |
| foreach ($core in $enabledCores) { | |
| $bitmask = $bitmask -bor (1 -shl $core) | |
| } | |
| $processHandle = [System.Diagnostics.Process]::GetProcessById($activePID) | |
| $processHandle.ProcessorAffinity = $bitmask | |
| $disabledCores = 0..($coreCount - 1) | Where-Object { $_ -notin $enabledCores } | |
| Write-Host "" | |
| Write-Host "Processor affinity set:" | |
| Write-Host " - Enabled cores: $($enabledCores -join ', ')" | |
| Write-Host " - Disabled cores: $($disabledCores -join ', ')" | |
| Write-Host "" | |
| if (!$autoKillOnProcessHang) { | |
| Write-Host "CPU monitor is disabled. Exiting now." | |
| exit | |
| } | |
| # Kill on process hang ------------- | |
| $elapsedTime = 0 | |
| while ($elapsedTime -lt $processHangTimeout) { | |
| $processHandleStillValid = [System.Diagnostics.Process]::GetProcesses() | Where-Object { $_.Id -eq $activePID } | |
| if (-not $processHandleStillValid) { | |
| Write-Host "Process handle is no longer valid. Exiting." | |
| exit | |
| } | |
| $processIsResponding = $processHandle.Responding | |
| if (!$processIsResponding) { | |
| $elapsedTime++ | |
| } else { | |
| $elapsedTime = 0 | |
| } | |
| if ($elapsedTime -gt ($processHangTimeout / 2)) { | |
| $host.privatedata.ProgressForegroundColor = "Yellow" | |
| } elseif ($elapsedTime -gt ($processHangTimeout / 3 * 2)) { | |
| $host.privatedata.ProgressForegroundColor = "Red" | |
| } else { | |
| $host.privatedata.ProgressForegroundColor = "Green" | |
| } | |
| Write-Progress -Activity "Monitoring $processNameInTaskManager.exe :: Process ID $activePID" -Status "Hang Detection Threshold ($elapsedTime/$processHangTimeout seconds)" -PercentComplete ($elapsedTime / $processHangTimeout * 100) | |
| Start-Sleep -Seconds 1 | |
| } | |
| Write-Host "Process hang detected..." | |
| # Restart process ------------------ | |
| if ($autoRestart) { | |
| Write-Host "Restarting..." | |
| Stop-Process -Id $activePID | |
| $thisScriptFile = $MyInvocation.MyCommand.Definition | |
| Invoke-Expression "cmd /c start powershell.exe -ExecutionPolicy Bypass -File '$thisScriptFile'" | |
| exit | |
| } else { | |
| Write-Host "Auto Restart is not enabled. Exiting now." | |
| Stop-Process -Id $activePID | |
| exit | |
| } | |
| } else { | |
| Write-Host "Process did not start within the timeout period of $timeout seconds." | |
| exit | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment