How Do You Monitor The CPU Utilization of a Process Using PowerShell?
Asked Answered
S

3

13

I'm trying to write a PowerShell script to monitor % CPU utilization of a SQL Server process. I'd like to record snapshots of this number every day so we can monitor it over time and watch for trends.

My research online said this WMI query should give me what I want:

Get-WmiObject -Query "SELECT PercentProcessorTime FROM win32_PerfFormattedData_PerfProc_Process WHERE Name='SqlServr'"

When I run the WMI query I usually get a value somewhere between 30-50% enter image description here

However, when I watch the process in Resource Monitor it usually averages at less than 1% CPU usage SQL Server Average CPU Utilization

I know the WMI query is simply returning snapshot of CPU usage rather than an average over a long period of time so I know the two aren't directly comparable. Even so, I think the snapshot should usually be less than 1% since the Resource Monitor average is less than 1%.

Does anybody have any ideas on why there is such a large discrepancy? And how I can get an accurate measurement of the CPU usage for the process?

Sexy answered 17/7, 2012 at 13:6 Comment(1)
Note The total CPU percent of all threads will add up to 100% times the number of logical cores. On a four physical core with hyperthreading (so eight logical cores) the total will be 800%.Dorindadorine
S
16

Everything I've learned about WMI and performance counters over the last couple of days.

WMI stands for Windows Management Instrumentation. WMI is a collection of classes registered with the WMI system and the Windows COM subsystem. These classes are known as providers and have any number of public properties that return dynamic data when queried.

Windows comes pre-installed with a large number of WMI providers that give you information about the Windows environment. For this question we are concerned with the Win32_PerfRawData* providers and the two wrappers that build off of it.

If you query any Win32_PerfRawData* provider directly you'll notice the numbers it returns are scary looking. That's because these providers give the raw data you can use to calculate whatever you want.

To make it easier to work with the Win32_PerfRawData* providers Microsoft has provided two wrappers that return nicer answers when queried, PerfMon and Win32_PerfFormattedData* providers.

Ok, so how do we get a process's % CPU utilization? We have three options:

  1. Get a nicely formatted number from the Win32_PerfFormattedData_PerfProc_Process provider
  2. Get a nicely formatted number from PerfMon
  3. Calculate the % CPU utilization for ourselves using Win32_PerfRawData_PerfProc_Process

We will see that there is a bug with option 1 so that it doesn't work in all cases even though this is the answer usually given on the internet.

If you want to get this value from Win32_PerfFormattedData_PerfProc_Process you can use the query mentioned in the question. This will give you the sum of the PercentProcessorTime value for all of this process's threads. The problem is that this sum can be >100 if there is more than 1 core but this property maxes out at 100. So, as long as the sum of all this process's threads is less than 100 you can get your answer by dividing the process's PercentProcessorTime property by the core count of the machine.

If you want to get this value from PerfMon in PowerShell you can use Get-Counter "\Process(SqlServr)\% Processor Time". This will return a number between 0 - (CoreCount * 100).

If you want to calculate this value for yourself the PercentProcessorTime property on the Win32_PerfRawData_PerfProc_Process provider returns the CPU time this process has used. So, you'll need to take two snapshots we'll call them s1 and s2. We'll then do (s2.PercentProcessorTime - s1.PercentProcessorTime) / (s2.TimeStamp_Sys100NS - s1.TimeStamp_Sys100NS).

And that is the final word. Hope it helps you.

Sexy answered 19/7, 2012 at 16:59 Comment(4)
Small correction: Performance counters (available in PerfMon etc.) are not a wrapper around the raw performance data classes in WMI, rather the other way around. (Underneath the performance data WMI Provider is reading the performance counters.)Dorindadorine
Do you have an article you could point me to that would describe this better?Sexy
For option #3, you might have to multiply by 100 and divide by # of processors to get a correct value.Sellingplater
When you devide, is that by the total core count i.e core + logical cores?Lathery
D
7

Your hypothesis is almost correct. A single thread (and a process will always have at least one thread) can have at most 100% for PercentProcessorTime but:

  • A process can have multiple threads.
  • A system can have multiple (logical) CPU cores.

Hence here (Intel i7 CPU with hyperthreading on) I have 8 logical cores, and the top 20 threads (filtering out totals) shows (with a little tidying up to make it readable):

PS > gwmi Win32_PerfFormattedData_PerfProc_Thread | 
    ?{$_.Name -notmatch '_Total'} | 
    sort PercentProcessorTime -desc | 
    select -first 20 | 
    ft -auto Name,IDProcess,IDThread,PercentProcessorTime

Name           IDProcess IDThread PercentProcessorTime
----           --------- -------- --------------------
Idle/6                 0        0                  100
Idle/3                 0        0                  100
Idle/5                 0        0                  100
Idle/1                 0        0                  100
Idle/7                 0        0                   96
Idle/4                 0        0                   96
Idle/0                 0        0                   86
Idle/2                 0        0                   68
WmiPrvSE/7#1        7420     6548                   43
dwm/4               2260     6776                    7
mstsc/2#1           3444     2416                    3
powershell/7#2      6352     6552                    0
conhost/0#2         6360     6368                    0
powershell/5#2      6352     6416                    0
powershell/6#2      6352     6420                    0
iexplore/7#1        4560     3300                    0
Foxit Reader/1       736     5304                    0
Foxit Reader/2       736     6252                    0
conhost/1#2         6360     1508                    0
Foxit Reader/0       736     6164                    0

all of which should add up to something like 800 for the last column.

But note this is all rounded to integers. Compare with the CPU column of Process Explorer (which doesn't round when View | Show Fractional CPU is selected) over a few processes. Note, much like win32_PerfFormattedData_PerfProc_Process the percentage value is normalised for the core count (and this is only part of the display):

A lot of processes are using a few hundreds of thousands of cycles, but not enough to round up to a single percent.

enter image description here

Dorindadorine answered 18/7, 2012 at 14:52 Comment(8)
Thanks for your response. When you Google my question the answer you find is to use the PercentProcessorTime property on the win32_PerfFormattedData_PerfProc_Process WMI object. So my question was trying to accomplish two things 1) pose my question and 2) show why the commonly accepted answer is wrong resulting in my needing a new one. So I believe my hypothesis is still correct for processes. What you say makes sense for threads but I'm still not sure how to find the % processor utilization for a process. What am I misunderstanding?Sexy
I think the problem boils down to this. Your proposal would work if the PercentProcessorTime for a process could go above 100 but it doesn't. It maxes out at 100 so what do you do once it hits the ceiling? One possible solution is to add up the PercentProcessorTime from all the child threads. Maybe that is what the PercentProcessorTime property on the WMI PerfMon proccess object does but it maxes out at 100?Sexy
@MarkRucker: To take this forward you need to define exactly what you want: "monitor CPU time" is not enough; do you want to graph or accumulated CPU-seconds? After that I would expect to perform several iterations of looking at the different counters (eg. what happens when I total win32_PerfFormattedData_PerfProc_Process.PercentProcessorTime across all processes: how much of an impact is the various levels of rounding? Would using Get-Counter be better (is WMI's CPU time distorting things), .... etc. before coming to a solution.Dorindadorine
@MarkRucker But first I would suggest by stating why you want this. Because the purpose is extremely important to get to the right information and mechanism – on re-reading the start of your question it seems you are very focused on how to get the information but have given no useful information on why you are trying to do it.Dorindadorine
I did some more experimentation and finally figured out the things I wanted to know. I went ahead and wrote a long explanation of everything I learned to get my answer. Thank you for your time and help.Sexy
Imho, there is a big problem with using Win32_PerfFormattedData_PerfProc_Thread - it is expensive and returns a ton of data. Running it once in powershell, I watch wmiprvse.exe peg one CPU core for a couple seconds. When my end goal is to evaluate per-process CPU usage, I don't want a solution that uses a lot of CPUCobos
@Cobos Don't forget to filter by process unless you want the data for all processes...Dorindadorine
Good point, forgot that wasn't what the user here was going after and I imagine that makes a big difference when you only want a single process. I'm actually trying to monitor all processes in a performant way.Cobos
H
0

Have you tryed Get-Counter ?

PS PS:\> Get-Counter "\Processus(iexplor*)\% temps processeur"

Timestamp                 CounterSamples
---------                 --------------
17/07/2012 22:39:25       \\jpbhpp2\processus(iexplore#8)\% temps processeur :
                          1,5568026751287

                          \\jpbhpp2\processus(iexplore#7)\% temps processeur :
                          4,6704080253861

                          \\jpbhpp2\processus(iexplore#6)\% temps processeur :
                          0

                          \\jpbhpp2\processus(iexplore#5)\% temps processeur :
                          4,6704080253861

                          \\jpbhpp2\processus(iexplore#4)\% temps processeur :
                          0

                          \\jpbhpp2\processus(iexplore#3)\% temps processeur :
                          0

                          \\jpbhpp2\processus(iexplore#2)\% temps processeur :
                          0

                          \\jpbhpp2\processus(iexplore#1)\% temps processeur :
                          1,5568026751287

                          \\jpbhpp2\processus(iexplore)\% temps processeur :
                          0

Be careful it depend on you locale test :

PS PS:\> Get-Counter -ListSet * | where {$_.CounterSetName -contains "processus"}
Hartzell answered 17/7, 2012 at 20:43 Comment(3)
It is my understanding that the Get-Counter cmdlet queries the same WMI objects I am using above. See cl.ly/image/022x1S2J461b which comes from bit.ly/Nw8JSq.Sexy
@MarkRucker You are correct, the WMI performance counter classes are another way to the same information.Dorindadorine
@Dorindadorine I agree, I wasn't asking. I was telling him that I was already using the Get-Counter values through WMI even though I wasn't using that cmdlet.Sexy

© 2022 - 2024 — McMap. All rights reserved.