Summary from last blog:
- Tip: An alias for Get-CimInstance is GCim, for Select-Object it's Select and for Get-WmiObject is GWmi.
- There are Raw and Formatted counters. Watch out for formula converting Raw samples to Formatted values.
NAMESPACE organization
The general organization of namespaces is as follows:Category (Class if you prefer)
Counter(s)
Instance(s)
Every Category has Counters but not all of the Counters have Instances. The full path to the desired value is called a __PATH:
Note: "." and "localhost" are synonyms.
Note: Knowing paths will allow us to move faster around as well as writing WMI queries as I will demonstrate later.
Putting .NET to work
UsingSystem.Diagnostics.PerformanceCounterCategory
class (which I prefer for fetching single values that update often) lets take two Categories as an example.Note: I saw a lot of questions regarding WMIRefresher that exists in VB and has no apparent counterpart in Powershell and this is it, IMO, since variable pointing to counter object holds (lightweight) connection to path reducing the overhead when fetching next value. Another good example of refreshing WMI data is described in Tip 10/c. This "trick" will do the fetch of entire Processor Information category in approximately 0.3s on my laptop which is about as fast as it gets. Remember that the first execution usually takes a while (couple of seconds).
.... there are 91 in total.
.... there are 35 in total.
So, Memory category has 35 Counters and no Instances which means you can fetch values directly:
Note: Available MBytes is ever updating value not dependant on number of samples. Thus calling NextValue() was unnecessary but good practice.
Fetching data from Processor Information category is a bit different:
Processor Information has 6 instances which means I am writing this on dual-core laptop with HT enabled (2 physical CPUs, 2 logical CPU's and the 2 Totals). It is rather interesting to play with these counters on proper servers. For now, important thing is to notice that "_Total" stands for entire box, "N, _Total" represents "Socket N" instance while "N,M" stands for "Socket,CPU" instance.
To fetch particular perf-counter value, I have to provide InstanceName:
Tip:There is an overload allowing you to write New-Object Diagnostics.PerformanceCounter("Processor Information", "% Processor Time", "_Total") instead of providing Category/InstanceName members via variables.
Let's check on New-Object members:
Note: It is always a good practice to read CounterHelp and make note of CounterType. This will tell you a lot about values obtained.
Note: Please check on previous blog, paragraph about Raw/Formatted counters and sampling.
Note: There is a plenitude of counters and you are free to explore them in search for one that suits your needs best. I.e. if you do not want to bother with Instances here, you can use Win32_PerfFormattedData_PerfOS_Processor Class which uses absolute Index to each CPU (see below):
So, how do we tell if Counter has Instances?
The answer is obviously in CategoryType member of PerformanceCounterCategory class which you should check while iterating.Note: The dot in MachineName stands for localhost.
You can check instance values directly with WMI too using their individual instance paths. Remember WMI classes: GWmi -List | Select Name | Where {$_.Name -match "Win32_PerfForm"}
Let's take Win32_PerfFormattedData_PerfOS_Processor for the example:
Note: Remember that Path property begins with two underscores.
Now that you know the path to a WMI instance, you can access it directly by converting the WMI path to a WMI object:
Note: __PROPERTY_COUNT : 24 means I trimmed some lines.
Pick one counter:
Note that '.' stands for 'localhost'. You can provide Server name here.
Note: You can also specify the full WMI path, including a machine name to access WMI objects on remote systems (provided you have sufficient access rights).
Tip: There is a hidden object property called "PSTypeNames" which will tell you the object type as well as the inheritance chain:
Of course, the type listed at the top is telling you the most:
PSTypeNames will work for all objects and might come handy navigating namespaces.
Note: You can do things with WMI objects, not just read counters. Check, for example, Win32_LogicalDisk device Chkdsk method:
This functionality as well as accessing remote machines is beyond scope of the document and mentioned here just for the sake of completeness.
It is also worth noting you can call WMI methods with CIM cmdlets. Please see this POWERTIP for details if you're interested.
Useful WMI links:
Powertip 1, auto-discovering online help for wmiPowertip 2, getting help on wmi methods
WQL
I mentioned earlier you can write your own WQL queries to fetch data from WMI objects. WQL is the WMI Query Language, a subset of the ANSI SQL with minor semantic changes.GWmi Win32_Process -Filter "Name like ""power%.exe""" translates to WQL query 'select * from Win32_Process where Name like "power%.exe"'. So, to get process owner for example:
Note: Make sure Add-Member ... -PassThrough line is not broken if you want this code to work.
Personally, I find WQL inadequate since it's missing aggregation functions thus I use it very rarely.
Summing it up
get classes:
GWmi -List
or
Get-CimClass | Select CIMClassName
or
[System.Diagnostics.PerformanceCounterCategory]::GetCategories()
shorten the list:
GWmi -List Win32_*memory* | Select Name
or
Get-CimClass | Select CIMClassName | Where {$_.CimClassName -match "memory"}
or
[System.Diagnostics.PerformanceCounterCategory]::GetCategories() | Where {$_.CategoryName -match "memory"} | Select CategoryName
list counters:
GWmi Win32_PhysicalMemory
or
GCim CIM_PhysicalMemory
or
(New-Object Diagnostics.PerformanceCounterCategory("Memory")).GetCounters("") | Select CounterName | Sort CounterName
and for WQL, write a query:
GWmi -Query 'select Manufacturer from Win32_PhysicalMemory where BankLabel = "BANK 0"'
or
GCim -Query 'Select Manufacturer from CIM_PhysicalMemory Where BankLabel = "BANK 0"'
Note: Opening communication and fetching objects from WMI server might take considerable amount of time. Counting CPU's takes at least few seconds on my boxes:
PS > Measure-Command {((GCim -Namespace root/CIMV2 -ClassName CIM_Processor).NumberOfLogicalProcessors | Measure-Object -Sum).Sum}
...
TotalSeconds : 2.2558991
...
Tip: The fastest way to learn how many CPUs there are on the box is
(GCim Win32_ComputerSystem).NumberOfLogicalProcessors
Note: In my experience, best time to fetch some value (if not cached) is about 0.3 seconds so that's what I'm aiming for always.
Conclusion:
To speed up fetching counter data, where available, I like to use System.Diagnostics .NET class:$System_CSpS = New-Object Diagnostics.PerformanceCounter("System", "Context Switches/sec")
$System_CSpS.NextValue()
Since some of the performance counters are empty upon first access, calling NextValue() is a good habit. Subsequent calls to $var.NextValue() are lightning fast so put it in variable. The next thing this technique is good for are values that are always there; such as amount of (free)RAM, Context Switches per second and so on. Although possible, I do not use this mechanism for values that might disappear, such as number of threads belonging to some process as process might die.
If you are not able to use System.Diagnostics or you prefer CIM approach you can always fall back to Tip 10/c:
PS > # Get instance of a class
PS > $p = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfOS_Processor
PS > # Perform get again by passing the instance received earlier, and get the updated properties. The value of $p remains unchanged.
PS > $p | Get-CimInstance | select PercentProcessorTime
Beware that for counters with instances, you need to supply InstanceName:
$InstanceName = "_Total"
$PI_PT = New-Object Diagnostics.PerformanceCounter("Processor Information", "% Processor Time")
$PI_PT.InstanceName = $InstanceName
#or $PI_PT = New-Object Diagnostics.PerformanceCounter("Processor Information", "% Processor Time", "_Total")
$PI_PT.NextValue()
If you are interested in measuring just one value, Get-Counter is your friend but I prefer TAB completion of CIM classes approach over iterating through paths (say,
(Get-Counter -listset memory).paths
).You can almost always accomplish the same thing using WMI and CIM cmdlest. Prefer CIM over WMI.
WQL is cumbersome and lacking many commands. Avoid.
Remember the hierarchy:
Category (Class if you prefer)
Counter(s)
Instance(s)
Every Category has Counters but not all of the Counters have Instances. Check before using.
Next blog will deal with several specific counters and the meaning of the values obtained in terms of performance.
In this series:
BLOG 1: PerfCounters infrastructure
BLOG 2: PerfCounters Raw vs. Formatted values
BLOG 3: PerfCounters, fetching the values
BLOG 4: PerfCounters, CPU perf data
BLOG 5: PerfCounters, Memory perf data
BLOG 6: PerfCounters, Disk/IO perf data
BLOG 7: PerfCounters, Network and Contention perf data
Can anyone explain why I get more PerformanceCounter instances returned when I use the PowerShell Get-Counter than when I use the C# function?
ReplyDeletePS C:\> (New-Object System.Diagnostics.PerformanceCounterCategory("SMB Client Shares")).GetInstanceNames()
*
_Total
PS C:\>
PS C:\>
PS C:\> Get-Counter -ListSet "SMB Client shares"
CounterSetName : SMB Client Shares
MachineName : .
CounterSetType : MultiInstance
Description : This counter set displays information about server shares that are being accessed by the client us
ing SMB protocol version 2 or higher.
Paths : {\SMB Client Shares(*)\Credit Stalls/sec, \SMB Client Shares(*)\Metadata Requests/sec, \SMB Client
Shares(*)\Avg. Data Queue Length, \SMB Client Shares(*)\Avg. Write Queue Length...}
PathsWithInstances : {\SMB Client Shares(\cl-sofs-smb\VMs3)\Credit Stalls/sec, \SMB Client Shares(\localhost
\IPC$)\Credit Stalls/sec, \SMB Client Shares(\cl-sofs-smb\VMs2)\Credit Stalls/sec, \SMB
Client Shares(\cl-sofs-smb\VMs1)\Credit Stalls/sec...}
Counter : {\SMB Client Shares(*)\Credit Stalls/sec, \SMB Client Shares(*)\Metadata Requests/sec, \SMB Client
Shares(*)\Avg. Data Queue Length, \SMB Client Shares(*)\Avg. Write Queue Length...}
PS C:\>
Get-Counter is showing PathsWithInstances and not just Paths. Please see https://technet.microsoft.com/en-us/library/hh849685.aspx for details.
DeleteThanks. How can I get PathsWithInstances returned with use of C#?
DeleteThanks. The mystery to solve is what C# code is hidden behind getting PathsWithInstances returned. Do anyone know?
Deletegreat
ReplyDelete