PerformanceCounters: reading the disk counters [David Gutierrez]

I see a lot of complaints about counters in the Logical Disk/Physical Disk categories always returning zero when you try to read them.  Specifically, using PerformanceCounter.NextValue with these counters always returns zero:

    % Disk Read Time
    % Disk Write Time
    % Idle Time
    % Disk Time
    Avg. Disk Queue Length
    Avg. Disk Read Queue Length
    Avg. Disk Write Queue Length

Two things happen when you call NextValue.  First, we read the raw value of the performance counter.  Second, we do some calculations on the raw value based on the counter type that the counter says it is.  If the calculated value is always zero, either we failed to read the counter or we failed to calculate it properly.  If we failed to read, well, you’re out of luck and there’s probably no way to work around the problem.  But if we failed to calculate it properly, then you have the option of doing the calculation yourself.

For the “% …” counters, unfortunately the bug is in the first step of reading the counters, so doing the calculations yourself won’t help.  However, the “Avg  Disk …” simply have bugs in their calculations, and I’m going to walk through the process of doing the calculations manually. 

First, you need to find the counter type.  The first thing I found was that the Avg counters are of type PERF_COUNTER_LARGE_QUEUELEN_TYPE:
But after some trial and error, I discovered that they are actually PERF_COUNTER_100NS_QUEUELEN_TYPE:

The next step is to figure out what the calculation for PERF_COUNTER_100NS_QUEUELEN_TYPE is.  A quick MSDN search yields, which tells us that the calculation looks like (X1-X0)/(Y1-Y0), where X is the counter data and Y is the 100ns Time.

Now we can finally implement the calculation ourselves:

    public static double Calculate100NsQueuelen(CounterSample oldSample, CounterSample newSample) {
        ulong n = (ulong) newSample.RawValue - (ulong) oldSample.RawValue;
        ulong d = (ulong) newSample.TimeStamp100nSec - (ulong) oldSample.TimeStamp100nSec;         return ((double) n) / ((double)d);

This process is somewhat of a pain, as documentation is not always good and it can be difficult to figure out which values to use in the calculations.  But if you compare your results to what perfmon reports and keeping trying, you can get it eventually.

Comments (2)