cancel
Showing results for 
Search instead for 
Did you mean: 
Create Post
Level 12

Top Ten Processes by CPU on Windows

Jump to solution

I'm trying to create a test only monitor that will return the top ten processes on a Windows machine based on CPU utilization.  I'm also trying to duplicate this for memory, but that should be trivial once the CPU monitor is created.

The basic WMI script monitor won't work because it requires a single process.  The goal is to easily find out which processes might be causing a high CPU alert without needing to connect to a VPN and RDP into the machine.  I've already created similar monitors for Linux using Perl, but Windows using VBScript has me stumped.

I've been primarily working with VBScript making a WMI call to win32_performatteddata_perfproc_process to retrieve the Name, IDProcess, and PercentProcessorTime fields.  The below code has been my most successful so far, but it isn't without its own problems - Namely that values add up to more than 100%.

 

Option Explicit
Dim objWMIService, objRefresher
Dim strComputer,strList
Dim colProcesses,objProcess

strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set objRefresher = CreateObject("WbemScripting.SWbemRefresher")
set colProcesses = objRefresher.AddEnum (objWMIService, "Win32_PerfFormattedData_PerfProc_Process").objectSet

objRefresher.Refresh
WScript.Sleep(1000)
objRefresher.Refresh

strList = "Name      Proc"
for each objProcess in colProcesses
    strList = strList & vbCr & objProcess.Name & "      " & objProcess.PercentProcessorTime
next

WScript.Echo strList

 

Obviously this isn't yet setup to return proper data to Solarwinds and is just a prototype for pulling the data.  Apparently the WMI Object doesn't return the proper data on the first call so the duplicate Refresh are needed and any less than 1 second sleep can cause 0/Null data to be returned for the PercentProcessorTime.

I've searched high and low, including Nagios and Cacti script repositories for something that will work.

I believe that if I could see the actual code used for the WMI Process monitor that I could get this to work.  I'm primarily a perl programmer, so any help on this would be appreciated.

0 Kudos
1 Solution
Level 12

Based on the assumption that the idle process is returning "bad" data, I was able to construct a script to return the top 0-10 processes in Windows based on CPU load.  I haven't tested it yet against another server, but I think that the issue originally posted can be considered solved.

We will be using this script as a test only monitor for triage in APM 4.2 after receiving an alert for high CPU load.  The idea is to remove the hassles of connecting to VPNs, looking up credentials, and taking the time to login.  Instead we can just go to the monitor template page, select a node and credentials from the lists and hit test.

 

Here is the basic script, it has an absolutely horrid selection sort, removes the Idle and Total values as well as any 0 CPU load processes then returns up to 10 top processes in descending order based on CPU load.  I know this is ugly as sin, but I'm not a VBScripter so had to make do with ugly (but functional) code.  It can be modified to return multiple values, although tracking won't be all that great in 4.2, but it is doable.

Option Explicit
Dim objWMIService, objRefresher
Dim strComputer, strList
Dim colProcesses, sliceProcesses()
Dim objProcess, tempProcess
Dim i, j, iPos, iMax, listLen

strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\"  _
                                & strComputer & "\root\cimv2")

Set objRefresher = CreateObject("WbemScripting.SWbemRefresher")
set colProcesses = objRefresher.AddEnum (objWMIService, _
                            "Win32_PerfFormattedData_PerfProc_Process").objectSet

objRefresher.Refresh
WScript.Sleep(1000)
objRefresher.Refresh

Redim Preserve sliceProcesses(0)
i = 0

'strip Idle, Total, and 0 values
for each objProcess in colProcesses
    if ((objProcess.PercentProcessorTime > 0) _
            and (not objProcess.name = "Idle") _
            and (not objProcess.Name = "_Total")) then
        Redim Preserve sliceProcesses(i)
        set sliceProcesses(i) = objProcess
        i = i+1
    end if
next

'Selection Sort, didn't feel like fighting with it to get a slice
for iPos = 0 to UBound(sliceProcesses)
    iMax = iPos
    
    for i = iPos+1 to UBound(sliceProcesses)
        
        if CInt(sliceProcesses(i).PercentProcessorTime) > _
                    CInt(sliceProcesses(iMax).PercentProcessorTime) then
            iMax = i
        end if
    next
    
    if (not iMax = iPos) then
        set tempProcess = sliceProcesses(iPos)
        set sliceProcesses(iPos) = sliceProcesses(iMax)
        set sliceProcesses(iMax) = tempProcess
    end if
next

if UBound(sliceProcesses) > 10 then listLen = 10 else listLen = UBound(sliceProcesses)

'Sad excuse for Array Slice
for i = 0 to listLen
        strList = strList + vbCR & sliceProcesses(i).Name & _
                "    " & sliceProcesses(i).PercentProcessorTime
next

WScript.echo "Message: " & strList
WScript.echo "Statistic: 0"

 

Thanks for the help and I look forward to even more new toys to play with in 4.3 😉

View solution in original post

0 Kudos
5 Replies
Level 12

Based on the assumption that the idle process is returning "bad" data, I was able to construct a script to return the top 0-10 processes in Windows based on CPU load.  I haven't tested it yet against another server, but I think that the issue originally posted can be considered solved.

We will be using this script as a test only monitor for triage in APM 4.2 after receiving an alert for high CPU load.  The idea is to remove the hassles of connecting to VPNs, looking up credentials, and taking the time to login.  Instead we can just go to the monitor template page, select a node and credentials from the lists and hit test.

 

Here is the basic script, it has an absolutely horrid selection sort, removes the Idle and Total values as well as any 0 CPU load processes then returns up to 10 top processes in descending order based on CPU load.  I know this is ugly as sin, but I'm not a VBScripter so had to make do with ugly (but functional) code.  It can be modified to return multiple values, although tracking won't be all that great in 4.2, but it is doable.

Option Explicit
Dim objWMIService, objRefresher
Dim strComputer, strList
Dim colProcesses, sliceProcesses()
Dim objProcess, tempProcess
Dim i, j, iPos, iMax, listLen

strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\"  _
                                & strComputer & "\root\cimv2")

Set objRefresher = CreateObject("WbemScripting.SWbemRefresher")
set colProcesses = objRefresher.AddEnum (objWMIService, _
                            "Win32_PerfFormattedData_PerfProc_Process").objectSet

objRefresher.Refresh
WScript.Sleep(1000)
objRefresher.Refresh

Redim Preserve sliceProcesses(0)
i = 0

'strip Idle, Total, and 0 values
for each objProcess in colProcesses
    if ((objProcess.PercentProcessorTime > 0) _
            and (not objProcess.name = "Idle") _
            and (not objProcess.Name = "_Total")) then
        Redim Preserve sliceProcesses(i)
        set sliceProcesses(i) = objProcess
        i = i+1
    end if
next

'Selection Sort, didn't feel like fighting with it to get a slice
for iPos = 0 to UBound(sliceProcesses)
    iMax = iPos
    
    for i = iPos+1 to UBound(sliceProcesses)
        
        if CInt(sliceProcesses(i).PercentProcessorTime) > _
                    CInt(sliceProcesses(iMax).PercentProcessorTime) then
            iMax = i
        end if
    next
    
    if (not iMax = iPos) then
        set tempProcess = sliceProcesses(iPos)
        set sliceProcesses(iPos) = sliceProcesses(iMax)
        set sliceProcesses(iMax) = tempProcess
    end if
next

if UBound(sliceProcesses) > 10 then listLen = 10 else listLen = UBound(sliceProcesses)

'Sad excuse for Array Slice
for i = 0 to listLen
        strList = strList + vbCR & sliceProcesses(i).Name & _
                "    " & sliceProcesses(i).PercentProcessorTime
next

WScript.echo "Message: " & strList
WScript.echo "Statistic: 0"

 

Thanks for the help and I look forward to even more new toys to play with in 4.3 😉

View solution in original post

0 Kudos
Level 13

From my observation it seems that problem can be in "System idle process" (PID = 0) because WMI returns not its own values for it, but something like total sum for all processes including system idle so as result it shows always CPU usage close to 100% for system idle regardless other processes consume CPU as well. If you need value for system idle, you can subtract sum of all others from 100%, otherwise simply ignore process with PID = 0.

At least we are experiencing such issues when using Win32_PerfRawData_PerfProc_Process.PercentProcessorTime, but I guess it will be similar with Win32_PerfFormattedData_PerfProc_Process.PercentProcessorTime.

This does make a bit of sense from what I recall seeing.  I'll give this a try on Wednesday when I get back to the office, but this is definitely promising. 

0 Kudos
Product Manager
Product Manager

We're currently working on something for the next release that should make these scripts unnecessary. Feel free to PM me for more details. 

0 Kudos

PM Sent.

I'd like to leave this open thread open in the hopes that someone will be able to provide an answer.  Primarily because we'd like to try and get something working right now, but also because this question has been asked all over the internet without any real answer.  And of course, I'd like an answer so I don't feel like I've wasted 12 hours for nothing.

0 Kudos