Using PowerShell to collect information for Storage Troubleshooting (Part 2: Performance Counters)

This is the second in a series of posts on how to utilize PowerShell to easily collect system information useful for troubleshooting Storage issues.

Note: In a later installment of this series, I’ll be showing how to utilize the information gathered in these scripts for troubleshooting some common storage issues.

In my first installment, I covered the collection of currently installed Hotfixes, and collection of specific system events related to storage. You can find the first installment here:

Storage Troubleshooting Part 1: Hotfixes and System Events

One troubleshooting step which typically comes into play with troubleshooting storage performance is collecting Performance Monitor (Perfmon) logs to assess the current system state, and look for causes of slow performance, such as lack of available memory, or excessive disk queue length.

Setting up a Perfmon log to capture information takes time and expertise for review, and I wanted a way to take a quick peek at the system to see if such an effort was needed. For example, if file copies seem “slow” and I were to look at the current Disk Queue length, it could tell me whether the perceived slowness is time spent waiting on the disk to do work.

Once you figure out how to access performance counters through PowerShell (which isn’t a lot of fun), it’s actually easy to use them to view information.

What I mean by this, is that finding them is hard, but using them is easy

Displaying Performance Counter Information:

In order to look at a specific counter, you need the path and name of the counter. For example, if I wanted to display the current Disk Queue Length for all disks, the following query in PowerShell would display this:

(Get-Counter "\\$Computer\PhysicalDisk(*)\Current Disk Queue Length").Countersamples

Which displays output, similar to the following:

Path InstanceName CookedValue
---- ------------ -----------
\\bman-pc\physicaldisk(0 c:) \current disk queue length 0 c: 0
\\bman-pc\physicaldisk(1 f:) \current disk queue length 1 f: 0
\\bman-pc\physicaldisk(2 i:) \current disk queue length 2 i: 0
\\bman-pc\physicaldisk(_total)\current disk queue length _total 0    

This can even be combined with the use of Where-Object to filter the results. For example, using the output above as a guide, if I only wished to display these counters for the C volume, I would use the following command to specify that I want only the instance name for Disk 0.

((Get-Counter "\\$Computer\PhysicalDisk(*)\Current Disk Queue Length" ).Countersamples | ? {$_.InstanceName -eq "0 c:"})  

Accessing the list of available counter paths

This is cool, but only of limited use unless you know how to find the path and name needed for accessing the counters. This is done by storing the output of Get-Counter in a variable, and then looking at the paths property. For example, if I wished to list all of the counters available on the Disk object, I would use the following Query:

$PhysCounter = get-counter -ListSet PhysicalDisk

Which yields the following list of paths exposed by this object which may be queried:

\PhysicalDisk(*)\Current Disk Queue Length
\PhysicalDisk(*)\% Disk Time
\PhysicalDisk(*)\Avg. Disk Queue Length
\PhysicalDisk(*)\% Disk Read Time
\PhysicalDisk(*)\Avg. Disk Read Queue Length
\PhysicalDisk(*)\% Disk Write Time
\PhysicalDisk(*)\Avg. Disk Write Queue Length
\PhysicalDisk(*)\Avg. Disk sec/Transfer
\PhysicalDisk(*)\Avg. Disk sec/Read
\PhysicalDisk(*)\Avg. Disk sec/Write
\PhysicalDisk(*)\Disk Transfers/sec
\PhysicalDisk(*)\Disk Reads/sec
\PhysicalDisk(*)\Disk Writes/sec
\PhysicalDisk(*)\Disk Bytes/sec
\PhysicalDisk(*)\Disk Read Bytes/sec
\PhysicalDisk(*)\Disk Write Bytes/sec
\PhysicalDisk(*)\Avg. Disk Bytes/Transfer
\PhysicalDisk(*)\Avg. Disk Bytes/Read
\PhysicalDisk(*)\Avg. Disk Bytes/Write
\PhysicalDisk(*)\% Idle Time
\PhysicalDisk(*)\Split IO/Sec                

Prefixing this with the computer name is all that is needed to use this to collect information from the counters.

PowerShell sample script

In the sample below, I’ve taken this a bit further, and create a table for each of the counter sets. There are example outputs below, followed by the script sample itself.

Example of Disk counters output:


Example of Processor counters output:


Example of Memory counters output:



Note: The script below can also be downloaded from the Microsoft Script Repository on Technet at the following link:


# Created with: SAPIEN Technologies, Inc., PrimalForms 2011 v2.0.11
# Created on: 8/14/2011 7:16 PM
# Created by: Bruce Langworthy
# Organization: Microsoft Corporation
# Filename: StoragePerformanceCounters.PS1   
# Note: The performance information displayed by these counters are the
# current values only, they do not show trends. This information
# should be used as a measurement only of the current system state.

# Retrieve the current Disk performance counter information.
$computer         = $ENV:Computername
$instance         = "_total"

@("\\$Computer\PhysicalDisk(*)\Current Disk Queue Length",
  "\\$Computer\PhysicalDisk(*)\% Disk Time",
  "\\$Computer\PhysicalDisk(*)\Avg. Disk Queue Length",
  "\\$Computer\PhysicalDisk(*)\Avg. Disk Read Queue Length",
  "\\$Computer\PhysicalDisk(*)\Avg. Disk Write Queue Length",
  "\\$Computer\PhysicalDisk(*)\Avg. Disk sec/Transfer"
  "\\$Computer\PhysicalDisk(*)\Avg. Disk sec/Read",
  "\\$Computer\PhysicalDisk(*)\Avg. Disk sec/Write") |% {
    (Get-Counter $_.replace("*",$instance)).CounterSamples } |
    Select-Object Path,CookedValue |
    Format-Table -AutoSize

# Retrieve the current Processor performance counter information.
$computer         = $ENV:Computername
$instance         = "_total"
@("\\$Computer\Processor(*)\% Processor Time",
  "\\$Computer\Processor(*)\% User Time",
  "\\$Computer\Processor(*)\% Privileged Time",
  "\\$Computer\Processor(*)\% DPC Time",
  "\\$Computer\Processor(*)\DPCs Queued/sec"
  "\\$Computer\Processor(*)\% Idle Time",
  "\\$Computer\Processor(*)\% Interrupt Time") |% {
    (Get-Counter $_.replace("*",$instance)).CounterSamples } |
    Select-Object Path,CookedValue |
    Format-Table -AutoSize

# Retreive the current Memory counter information
$computer         = $ENV:Computername
$instance         = "_total"
@("\\$Computer\Memory\Page Faults/sec",
  "\\$Computer\Memory\Available Bytes",
  "\\$Computer\Memory\Committed Bytes",
  "\\$Computer\Memory\Commit Limit",
  "\\$Computer\Memory\Free System Page Table Entries"
  "\\$Computer\Memory\Pool Paged Resident Bytes",
  "\\$Computer\Memory\Available MBytes") |% {
    (Get-Counter $_.replace("*",$instance)).CounterSamples } |
    Select-Object Path,CookedValue |
    Format-Table -AutoSize

#The following examples shows how to list the available paths to query for several counter sets
$PhysCounter  = get-counter -ListSet PhysicalDisk

#Memory Counters
$MemCounter   = get-counter -ListSet Memory

#CPU Counters
$CpuCounter   = get-counter -ListSet Processor

Follow @BruceLangworthy