PowerShell/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/CounterTestHelperFunctions.ps1

300 lines
9.5 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
<############################################################################################
# File: CounterTestHelperFunctions.ps1
# Provides functions common to the performance counter Pester tests.
############################################################################################>
# Create a helper class providing facilities for translation of
# counter names and counter paths
$helperSource = @"
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
public class TestCounterHelper
{
private const long PDH_MORE_DATA = 0x800007D2L;
private const int PDH_MAX_COUNTER_NAME = 1024;
private const int PDH_MAX_COUNTER_PATH = 2048;
private const string SubKeyPath = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009";
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct PDH_COUNTER_PATH_ELEMENTS
{
[MarshalAs(UnmanagedType.LPWStr)]
public string MachineName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ObjectName;
[MarshalAs(UnmanagedType.LPWStr)]
public string InstanceName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ParentInstance;
public UInt32 InstanceIndex;
[MarshalAs(UnmanagedType.LPWStr)]
public string CounterName;
}
[DllImport("pdh.dll", CharSet = CharSet.Unicode)]
private static extern uint PdhMakeCounterPath(ref PDH_COUNTER_PATH_ELEMENTS pCounterPathElements,
StringBuilder szFullPathBuffer,
ref uint pcchBufferSize,
UInt32 dwFlags);
[DllImport("pdh.dll", CharSet = CharSet.Unicode)]
private static extern uint PdhParseCounterPath(string szFullPathBuffer,
IntPtr pCounterPathElements, //PDH_COUNTER_PATH_ELEMENTS
ref IntPtr pdwBufferSize,
uint dwFlags);
[DllImport("pdh.dll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName,
uint dwNameIndex,
System.Text.StringBuilder szNameBuffer,
ref uint pcchNameBufferSize);
private string[] _counters;
public TestCounterHelper(string[] counters)
{
_counters = counters;
}
public string TranslateCounterName(string name)
{
var loweredName = name.ToLowerInvariant();
for (var i = 1; i < _counters.Length - 1; i += 2)
{
if (_counters[i].ToLowerInvariant() == loweredName)
{
try
{
var index = Convert.ToUInt32(_counters[i - 1], CultureInfo.InvariantCulture);
var sb = new StringBuilder(PDH_MAX_COUNTER_NAME);
var bufSize = (uint)sb.Capacity;
var result = PdhLookupPerfNameByIndex(null, index, sb, ref bufSize);
if (result == 0)
return sb.ToString().Substring(0, (int)bufSize - 1);
}
catch
{
// do nothing, we just won't translate
}
break;
}
}
// return original path if translation failed
return name;
}
public string TranslateCounterPath(string path)
{
var bufSize = new IntPtr(0);
var result = PdhParseCounterPath(path,
IntPtr.Zero,
ref bufSize,
0);
if (result != 0 && result != PDH_MORE_DATA)
return path;
IntPtr structPointer = Marshal.AllocHGlobal(bufSize.ToInt32());
try
{
result = PdhParseCounterPath(path,
structPointer,
ref bufSize,
0);
if (result == 0)
{
var cpe = Marshal.PtrToStructure<PDH_COUNTER_PATH_ELEMENTS>(structPointer);
cpe.ObjectName = TranslateCounterName(cpe.ObjectName);
cpe.CounterName = TranslateCounterName(cpe.CounterName);
var sb = new StringBuilder(PDH_MAX_COUNTER_NAME);
var pathSize = (uint)sb.Capacity;
result = PdhMakeCounterPath(ref cpe, sb, ref pathSize, 0);
if (result == 0)
return sb.ToString().Substring(0, (int)pathSize - 1);
}
}
finally
{
Marshal.FreeHGlobal(structPointer);
}
// return original path if translation failed
return path;
}
}
"@
if ( $IsWindows )
{
Add-Type -TypeDefinition $helperSource
}
# Strip off machine name, if present, from counter path
function RemoveMachineName
{
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]
$path
)
if ($path.StartsWith("\\"))
{
return $path.SubString($path.IndexOf("\", 2))
}
else
{
return $path
}
}
# Retrieve the counters array from the Registry
function GetCounters
{
if ( $IsWindows )
{
$key = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage'
return (Get-ItemProperty -Path $key -Name Counter).Counter
}
}
# Translate a counter name from English to a localized counter name
function TranslateCounterName
{
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]
$counterName
)
$counters = GetCounters
if ($counters -and ($counters.Length -gt 1))
{
$counterHelper = New-Object -TypeName "TestCounterHelper" -ArgumentList (, $counters)
return $counterHelper.TranslateCounterName($counterName)
}
return $counterName
}
# Translate a counter path from English to a localized counter path
function TranslateCounterPath
{
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]
$path
)
$counters = GetCounters
if ($counters -and ($counters.Length -gt 1))
{
$counterHelper = New-Object -TypeName "TestCounterHelper" -ArgumentList (, $counters)
$rv = $counterHelper.TranslateCounterPath($path)
# if our original path had no machine name,
# we don't want one on our translated path
if (-not $path.StartsWith("\\"))
{
$rv = RemoveMachineName $rv
}
return $rv
}
return $path
}
# Compare two DateTime objects for relative equality.
#
# Exporting lops fractional milliseconds off the time stamp,
# so simply comparing the DateTime values isn't sufficient
function DateTimesAreEqualish
{
param (
[Parameter(Mandatory=$true)]
[ValidateNotNull()]
[DateTime]
$dtA,
[Parameter(Mandatory)]
[ValidateNotNull()]
[DateTime]
$dtB
)
$span = $dtA - $dtB
return ([math]::Floor([math]::Abs($span.TotalMilliseconds)) -eq 0)
}
# Compare the content of counter sets
function CompareCounterSets
{
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$setA,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$setB
)
$setA.Length | Should -Be $setB.Length
# Depending on the kinds of counters used, the first record in
# exported counters are likely to have embty items, so we'll
# start comparing at the second item.
#
# Note that this is not a bug in either the cmdlets or the tests
# script, but rather is the behavior of the underlying Windows
# PDH functions that perform the actual exporting of counter data.
for ($i = 1; $i -lt $setA.Length; $i++)
{
$setA[$i].CounterSamples.Length | Should -Be $setB[$i].CounterSamples.Length
$samplesA = ($setA[$i].CounterSamples | Sort-Object -Property Path)
$samplesB = ($setB[$i].CounterSamples | Sort-Object -Property Path)
(DateTimesAreEqualish $setA[$i].TimeStamp $setB[$i].TimeStamp) | Should -BeTrue
for ($j = 0; $j -lt $samplesA.Length; $j++)
{
$sampleA = $samplesA[$j]
$sampleB = $samplesB[$j]
(DateTimesAreEqualish $sampleA.TimeStamp $sampleB.TimeStamp) | Should -BeTrue
$sampleA.Path | Should -BeExactly $sampleB.Path
$sampleA.CookedValue | Should -Be $sampleB.CookedValue
}
}
}
function SkipCounterTests
{
if ([System.Management.Automation.Platform]::IsLinux -or
[System.Management.Automation.Platform]::IsMacOS -or
[System.Management.Automation.Platform]::IsIoT)
{
return $true
}
return $false
}