April 21st, 2005

Why Does My Performance Monitoring Script Keep Returning the Same Incorrect Values?

Hey, Scripting Guy! Question

Hey, Scripting Guy! I’m using a script to monitor process performance, but every time I run the script the percent processor time for all the processes comes out as 0. What am I doing wrong?

— JL

SpacerHey, Scripting Guy! AnswerScript Center

Hey, JL. We’ve simplified your script a bit to better illustrate the problem you’re running into. (And don’t feel bad: this is a very common problem people encounter when trying to measure performance counters over time.) Here’s a modified version of your script, one that returns the values of the Name and PercentProcessorTime properties for each process running on a computer:

strComputer = “.”
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)

Set colItems = objWMIService.ExecQuery _ (“Select * From Win32_PerfFormattedData_PerfProc_Process”)

For i = 1 to 5 For Each objItem in colItems Wscript.Echo objItem.Name & ” — ” & objItem.PercentProcessorTime Next Wscript.Echo Wscript.Sleep 5000 Next

When you run this-and no matter how many times you run it-you get back output that looks like this:

Idle — 0
System — 0
smss — 0
csrss — 0
winlogon — 0
services — 0
lsass — 0

So what’s the deal? Are your processes really using no processor time whatsoever (that’d be nice, wouldn’t it?) or is the Win32_PerfFormattedData_PerfProc_Process class simply broken?

As it turns out, neither one. Instead, here’s the problem. You begin by using ExecQuery to return performance counter data for all the processes and their properties. So far so good, with one minor caveat: for some reason, more often than not the first time you query a performance monitoring class you’ll get back either 0 or Null for all the property values. Why? To be honest, we don’t know; that’s just the way it works.

But that’s OK, because the next time you query a performance monitoring class (within the same script, at least) you will get back correct values. The only problem is that you never query Win32_PerfFormattedData_PerfProc_Process a second time. Take a look at your script again. You query the Win32_PerfFormattedData_PerfProc_Process class, and then you immediately enter a For Next loop that loops around 5 times. However, you never reissue your query while inside the loop. When you first query Win32_PerfFormattedData_PerfProc_Process the PercentProcessorTime for all your processes is likely to be 0. Because you never reissue your query, the property value never changes. That’s why you get 0 each time.

In fact, getting 0 is more an artifact of the way the performance monitoring classes work than anything else. Suppose you got a 77 instead. In that case each time through the loop the value would still be reported as 77, even if the process had long since been terminated. That’s because you’re still working with the original property values, not the current property values. Does that make sense? Imagine if back in 1790 we queried the United States for a list of all the states in the Union. And suppose we never issued that query again. If anyone ever asked, we’d simply report back the original 13 states. That’s because we never went out and got updated information.

So how do you fix the problem? Well, the best way to do this is to use the new SWbemRefresher object, found only on Windows XP and Windows Server 2003. (Which isn’t really a problem seeing as how the Win32_PerfFormattedData_PerfProc_Process and other formatted performance classes are also found only on Windows XP and Windows Server 2003.) We won’t explain the workings of SWbemRefresher in any detail today; for more information, see the WMI SDK on MSDN. However, here’s a revised script that will likely report null values the first time through the loop, but from then on will report correct processor use values:

strComputer = “.”
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)

Set objRefresher = CreateObject(“WbemScripting.SWbemRefresher”) Set colItems = objRefresher.AddEnum _ (objWMIService, “Win32_PerfFormattedData_PerfProc_Process”).objectSet

For i = 1 to 5 objRefresher.Refresh For Each objItem in colItems Wscript.Echo objItem.Name & ” — ” & objItem.PercentProcessorTime Next Wscript.Echo Wscript.Sleep 5000 Next

This time around your output will look something like this:

OUTLOOK — 5
WINWORD — 23
cmd — 0
iexplore — 3
MSNGather — 1
IPClient — 0
cscript – 1

So what did we do different in this script? Well, we created an instance of the SWbemRefresher object and then added the Win32_PerfFormattedData_PerfProc_Process class to this instance; all that happens with these lines of code:

Set objRefresher = CreateObject(“WbemScripting.SWbemRefresher”)
Set colItems = objRefresher.AddEnum _
    (objWMIService, “Win32_PerfFormattedData_PerfProc_Process”).objectSet

We then create a For Next loop, just like we did before. But notice the first line of code in our For Next loop:

objRefresher.Refresh

The Refresh method instructs SWbemRefresher to go out and grab the current values for any classes we’ve told it to keep track of. (And, as you recall, we used the AddEnum method to tell SWbemRefresher to keep track of the Win32_PerfFormattedData_PerfProc_Process class.) The Refresh method will grab the current values for all the properties of the Win32_PerfFormattedData_PerfProc_Process class and echo those to the screen. When we loop around the next time, the Refresh method will, again, go out and grab the current values and echo those to the screen. In other words, we’re essentially issuing a new query each time, and thus consistently grabbing the latest values.

Hopefully you’re not confused by all this; it actually makes sense once you catch on to how it works. Give it a try and you should be able to figure out it out. In the meantime, we’ll see what we can do about getting some additional documentation on SWbemRefresher posted to the Script Center.

Author

0 comments

Discussion are closed.