CPU hogging script

Hi Guys! how would you approach the following? as I’m struggling to get what I need at the min:

  1. Check CPU Use for a named Process (array of processes using the same name)

  2. If one of these processes passes the threshold of 85%, store PID to Variable,

  3. Check again based on PID every 30 seconds, if still over the threshold after 5 minutes. Kill based on PID

My Code for this at the moment ($data.cpu doesn’t work btw, I know this but can’t figure out how to pull it):

$CPUThreshold = 15
$SamplesIntervalInSeconds = 30
$MaximumSamples = 10
$SuccessiveMatchingSamples = 0

While($true){ 
$data = Get-Counter -ErrorAction SilentlyContinue '\Process(*)\% Processor Time' | Select-Object -ExpandProperty countersamples| Select-Object -Property instancename, cookedvalue| ? {$_.instanceName -match "svchost"} | Sort-Object -Property cookedvalue -Descending| Select-Object -First 3| ft InstanceName,@{L='CPU';E={($_.Cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')}}
If( $data.cpu -gt $CPUThreshold){
$SuccessiveMatchingSamples++
If($SuccessiveMatchingSamples -gt $MaximumSamples){
echo "sasdas"
$SuccessiveMatchingSamples = 0
}
}
else{
$SuccessiveMatchingSamples = 0
} 
Start-Sleep -Seconds $SamplesIntervalInSeconds
}

 

Thanks in advance!

Tom

Hey Tom,

Start by cleaning up your data query, especially Format-Table which kills the pipeline, it only makes things pretty in the console. Take a look at Powershell Gotchas in Free Resources.

So take this:

$data = Get-Counter -ErrorAction SilentlyContinue '\Process(*)\% Processor Time' | 
        Select-Object -ExpandProperty countersamples | 
        Select-Object -Property instancename, cookedvalue| ? {$_.instanceName -match "svchost"} | 
        Sort-Object -Property cookedvalue -Descending| Select-Object -First 3 | 
        ft InstanceName,@{L='CPU';E={($_.Cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')}}

and do something more like this:

$data = Get-Counter -ErrorAction SilentlyContinue '\Process(*)\% Processor Time' | 
        Select-Object -ExpandProperty CounterSamples | 
        Where-Object -FilterScript {$_.instanceName -match "svchost" -and $_.CookedValue -gt 0} |
        Select-Object -Property *, 
                                @{Name='CPU';Expression={(($_.Cookedvalue / 100) / $env:NUMBER_OF_PROCESSORS).ToString('P')}}

Hi Rob,

Thanks for your input, I have since found I can get the outputs in variables as I wanted to like this:

$ProcessData = (Get-Counter '\Process(svchost*)\% Processor Time').Countersamples | 
Sort cookedvalue -Desc | Select-Object -First 1|
Where-Object -FilterScript {$_.CookedValue -gt 15}

$Name = $ProcessData.InstanceName
$CPU = ($ProcessData.cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')

echo "$Name $CPU"

I now have a new issue! I’m tracking the Process and attempting to kill it if it continues to Hog the CPU… but there are multiple instances of the same name. I presume something can be done with the process PID, any ideas?

OK!!

Think I have done what I want the script to do, can anyone spot any issues with this code before I test it in a live environment?

I’ve used a Microsoft Developer tool CPU Stress to test it on my laptop (Tools To Simulate CPU / Memory / Disk Load | Microsoft Docs)

Thanks All

$SuccessiveMatchingSamples = 0
$MaximumSamples = 10
$CPULimit = 30
$CheckCycleFrequency = 1
$AfterKillWait = 60
$KillCount = 0
clear

while($true){
$ProcessData = (Get-Counter -ErrorAction SilentlyContinue '\Process(CPUSTRES*)\% Processor Time').Countersamples | Sort cookedvalue -Desc | Select-Object -First 1| Where-Object -FilterScript {$_.CookedValue -gt 0}
$Name = $ProcessData.InstanceName
$CPU = ($ProcessData.cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')
$P1DDate = (Get-Process -Name $Name | Sort-Object -Property cpu -Descending| Select -First 1)
$P1D = $P1DDate.ID

If($CPU -gt $CPULimit)
{
echo "$Name $P1D is Over $CPULimit%, CPU is: $CPU"
$ProcessData2 = (Get-Counter -ErrorAction SilentlyContinue '\Process(CPUSTRES*)\% Processor Time').Countersamples | Sort cookedvalue -Desc | Select-Object -First 1| Where-Object -FilterScript {$_.CookedValue -gt 0}
$CPU2 = ($ProcessData2.cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')
$P1D2Date = (Get-Process -Name $Name | Sort-Object -Property cpu -Descending| Select -First 1)
$P1D2 = $P1D2Date.ID
If($P1D2 = $P1D)
{
echo "$Name $P1D & $Name $P1D2 the Same, Counting as a Matching Sample."
$SuccessiveMatchingSamples++
echo "Current Count is: $SuccessiveMatchingSamples"
If($SuccessiveMatchingSamples -gt $MaximumSamples)
{
Clear
echo "Killing Process $Name with PID $P1D"
$KillCount++
echo "Current Kill Count is: $KillCount"
$SuccessiveMatchingSamples = 0
stop-process -id $P1D
Start-Sleep -Seconds $AfterKillWait
}
}
else
{
Clear
$SuccessiveMatchingSamples = 0
}
}
else
{
Clear
$SuccessiveMatchingSamples = 0
}
Start-Sleep -Seconds $CheckCycleFrequency
}

Glad it’s working, but it is recommended to use indention, which makes it much easier to see how the code is executing:

$SuccessiveMatchingSamples = 0
$MaximumSamples = 10
$CPULimit = 30
$CheckCycleFrequency = 1
$AfterKillWait = 60
$KillCount = 0
clear

while ($true){
    $ProcessData = (Get-Counter -ErrorAction SilentlyContinue '\Process(CPUSTRES*)\% Processor Time').Countersamples | 
                   Sort cookedvalue -Desc | 
                   Select-Object -First 1| 
                   Where-Object -FilterScript {$_.CookedValue -gt 0}

    $Name = $ProcessData.InstanceName
    $CPU = ($ProcessData.cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')
    $P1DDate = (Get-Process -Name $Name | Sort-Object -Property cpu -Descending| Select -First 1)
    $P1D = $P1DDate.ID

    If ($CPU -gt $CPULimit) {
        echo "$Name $P1D is Over $CPULimit%, CPU is: $CPU"
        $ProcessData2 = (Get-Counter -ErrorAction SilentlyContinue '\Process(CPUSTRES*)\% Processor Time').Countersamples | 
                        Sort cookedvalue -Desc | 
                        Select-Object -First 1| 
                        Where-Object -FilterScript {$_.CookedValue -gt 0}

        $CPU2 = ($ProcessData2.cookedvalue/100/$env:NUMBER_OF_PROCESSORS).toString('P')
        $P1D2Date = (Get-Process -Name $Name | Sort-Object -Property cpu -Descending| Select -First 1)
        $P1D2 = $P1D2Date.ID

        If ($P1D2 = $P1D) {
            echo "$Name $P1D & $Name $P1D2 the Same, Counting as a Matching Sample."
            $SuccessiveMatchingSamples++
            echo "Current Count is: $SuccessiveMatchingSamples"

            If ($SuccessiveMatchingSamples -gt $MaximumSamples) {
                Clear
                echo "Killing Process $Name with PID $P1D"
                $KillCount++
                echo "Current Kill Count is: $KillCount"
                $SuccessiveMatchingSamples = 0
                stop-process -id $P1D
                Start-Sleep -Seconds $AfterKillWait
            }
        }
        else
        {
            Clear
            $SuccessiveMatchingSamples = 0
        }
    }
    else {
        Clear
        $SuccessiveMatchingSamples = 0
    }

    Start-Sleep -Seconds $CheckCycleFrequency
}

EDIT: This is incorrect:

If ($P1D2 = $P1D) {...

the comparison operator is -eq