Comparing Performance Counters
A common TSG step is to look at some perf counter, wait n minutes, then look at it again. When we’re talking about pools, it gets a little more involved. PerfMon.exe isn’t the most easy to automate (you can save the counterset to an .msc, then copy that file across machines, but you still have to log into the machine to get the local data.)
Here’s another way:
function Test-PerfCounter
{
<#
.synopsis
Capture change over time for a perf counter from specified computer(s).
.description
Takes a snapshot of perf counters upon invocation, wait specified minutes, take second snapshot, compare values.
.parameter ComputerName
Specified computer(s) from which to collect perf counter data. Defaults to $env:Computername
.parameter Counter
Name of perf counter from which to collect data. Required.
.parameter Minutes
Wait interval between first and second samples. Defaults to 5.
.parameter Delta
Threshold for difference between first and second runs beyond which test is marked as failure. Defaults to 0.01.
.outputs
PSObject with properties
- [string] ComputerName
- ([int] or [float]) Value1
- [Datetime] Time1
- ([int] or [float]) Value1
- [Datetime] Time1
- [bool] Pass
.example
Test-PerfCounter -Counter '\Processor Information(_Total)\% Processor Time'
#>
param (
[parameter(ValueFromPipeline=$true)][string[]]$ComputerName = @($env:ComputerName.ToLower()),
[string]$Counter = $null,
[int]$Minutes = 5,
[float]$Delta = 0.01
);
begin
{
if (!$Counter)
{
$msg = "$($MyInvocation.Line) -Counter not specified, required.";
Write-Error -Message $msg -ErrorAction SilentlyContinue;
Write-Warning -Message $msg;
return;
break __outOfScript;
} # if (!$Counter)
if ($Minutes -le 0)
{
$msg = "$($MyInvocation.MyCommand.Name) -Minutes must be positive, required.";
Write-Error -Message $msg -ErrorAction SilentlyContinue;
Write-Warning -Message $msg;
return;
break __outOfScript;
} # if ($Minutes -le 0)
$now = Get-Date;
} # begin
process {
#region get first pass counters
Write-Progress -Activity "($now) $($MyInvocation.Line)" -Status "Pass 1: $Counter";
[Object[]]$run1 = Get-Counter -ComputerName $ComputerName -Counter $Counter |
ForEach-Object {
$Timestamp = $_.Timestamp;
$_.CounterSamples |
Select-Object -Property @{
n = 'ComputerName';
e = { $_[0].Path -replace '^\\\\' -replace '\\.*'; }
}, @{
n = 'Value';
e = { $_[0].CookedValue; }
}, TimeStamp
} |
Sort-Object ComputerName;
if (!$run1 -and !$run1.Count)
{
$msg = "$($MyInvocation.Line) did not return any perf counters, stopping.";
Write-Error -Message $msg -ErrorAction SilentlyContinue;
Write-Warning -Message $msg;
return;
break __outOfScript;
} # if (!$run1...
if ($DebugPreference -eq 'Continue') { $Global:__run1 = $run1; }
#endregion
#region Sleep for specified time
$now = Get-Date;
$stop = $now + (New-TimeSpan -Minutes $Minutes);
while ($now -lt $stop)
{
$now = Get-Date;
Write-Progress -SecondsRemaining ($stop - $now).TotalSeconds -Activity "($now) $($MyInvocation.Line)" -Status "Sleeping for $Minutes minute(s).";
Start-Sleep -Seconds 10;
} # while ($now -lt $stop)
#endregion
#region get second pass counters
Write-Progress -Activity "($now) $($MyInvocation.Line)" -Status "Pass 2: $Counter";
[Object[]]$run2 = Get-Counter -ComputerName $ComputerName -Counter $Counter |
ForEach-Object {
$Timestamp = $_.Timestamp;
$_.CounterSamples |
Select-Object -Property @{
n = 'ComputerName';
e = { $_[0].Path -replace '^\\\\' -replace '\\.*'; }
}, @{
n = 'Value';
e = { $_[0].CookedValue; }
}, TimeStamp
} |
Sort-Object ComputerName;
if (!$run2 -and !$run2.Count)
{
$msg = "$($MyInvocation.Line) did not return any perf counters, stopping.";
Write-Error -Message $msg -ErrorAction SilentlyContinue;
Write-Warning -Message $msg;
return;
break __outOfScript;
} # if (!$run2 ...
if ($DebugPreference -eq 'Continue') { $Global:__run2 = $run2; }
#endregion
#region output data
for ($i = 0; $i -lt $run1.Count; $i++)
{
$counter1 = $run1[$i];
$counter2 = $run2 | ? { $_.ComputerName -eq $counter1.Computername; }
New-Object -TypeName PsObject -Property @{
ComputerName = $counter1.Computername;
Value1 = $counter1.Value;
Time1 = $counter1.Timestamp;
Value2 = $counter2.Value;
Time2 = $counter2.TimeStamp;
Pass = [Math]::Abs($counter1.Value - $counter2.Value) -le $Delta;
} |
Select-Object -Property ComputerName, Value1, Time1, Value2, Time2, Pass;
} # for ($i = 0...
#endregion
} # process
} # function Test-PerfCounter