Windows gives us plenty of built-in performance counters for monitoring various aspects of processes. However, there might be scenarios where built-in counters don’t fulfill specific monitoring requirements. This is where custom performance counters come into play, and PowerShell can really help with that.

Whether you’re trying to improve performance, make better use of resources, or just trying to track down some issue that’s causing an error on your system, when you’re working with your Windows OS, you’re going to be spending a lot of time working with processes. And process monitoring is an important aspect of working with processes. It can really highlight the issue quickly when you can identify which running process is consuming all of the CPU or RAM.

In this article, we will explore custom performance counters in PowerShell and how they can be used to monitor processes in a more tailored manner. This article is intended for system administrators who are learning PowerShell and are familiar with the basics of processes running on Windows.

Setting up Your System

This article was written assuming that you’re running PowerShell 7, with VS Code as your editor and the PowerShell extension. PowerShell 7 is cross-platform, so it can be used on Windows, macOS, and Linux. We recommend using Visual Studio Code (VSCode) as your editor, as it’s just an awesome environment for doing anything with PowerShell – it has an integrated terminal, intellisense, and great debugging tools. If you’re not running with this setup yet, well – it’s time to get started! Here’s basic instructions on how to install PowerShell for Windows, Mac or Linux

Overview of Performance Counters in PowerShell

Windows provides built-in performance counters that allow you to monitor various aspects of processes, such as CPU usage, memory consumption, and I/O operations. PowerShell is pretty great at making these counters accessible from the command line using easy command syntax. They can be accessed using the Get-Counter cmdlet. However, built-in performance counters may not cover every monitoring requirement, especially when you need to track custom metrics or specific application behavior. This is where custom performance counters become essential.

When I’m digging deep into performance counters

Use cases for custom performance counters

Custom performance counters gives system administrators and IT professionals some serious power to amp up their monitoring game. Really, anything is possible. So when would you use it? Some common use cases include:

  • Monitoring application-specific metrics: Custom performance counters allow you to track metrics specific to your applications, such as the number of requests processed or the number of transactions completed.
  • Tracking resource usage trends over time: Custom performance counters can help you track how resources are consumed over time, enabling you to identify patterns and make informed decisions about resource allocation and optimization.
  • Customizing monitoring for specific requirements: With custom performance counters, you can tailor your monitoring approach to meet the unique needs of your organization, providing better visibility into your processes and systems.

Creating custom performance counter categories

Before you can create a custom performance counter, you must define a custom performance counter category. This category will serve as a container for your custom counters, making them easier to manage and organize.

What is a custom performance counter category?

Custom performance counter categories are groupings of related custom performance counters. They help you organize your custom counters and provide a logical structure for accessing them.

How to create and register a custom performance counter category in PowerShell

To create your category, you need to define its name, description, and the counters it will contain. Then, you can register it using the [System.Diagnostics.PerformanceCounterCategory]::Create() method.

$categoryName = "CustomProcessMonitor"
$categoryHelp = "A custom category for monitoring specific process aspects"
$counterName = "CustomCounter"
$counterHelp = "A custom counter for process monitoring"
$counterType = [System.Diagnostics.PerformanceCounterType]::NumberOfItems32

# Register the custom performance counter
$counterData = New-Object System.Diagnostics.CounterCreationData($counterName, $counterHelp, $counterType)
$counterDataCollection = New-Object System.Diagnostics.CounterCreationDataCollection
$counterDataCollection.Add($counterData)
[System.Diagnostics.PerformanceCounterCategory]::Create($categoryName, $categoryHelp, $counterDataCollection)

Creating custom performance counters in PowerShell

Now that you have a custom performance counter category, you can create custom performance counters within it. Custom performance counters are user-defined counters that track specific metrics or aspects of processes. They can be used to monitor application-specific information, resource usage trends, or any other custom data you need to track.

Defining and register your custom counter

To create a custom performance counter, you need to define its name, description, and type. You can then register the counter within your custom performance counter category.

$process = Start-Process "notepad.exe" -PassThru

# Update the custom performance counter
$counter = New-Object System.Diagnostics.PerformanceCounter($categoryName, $counterName, $process.ProcessName, $false)
$counter.RawValue = 0

This code block does two things:

  1. $process = Start-Process "notepad.exe" -PassThruThis line starts a new instance of Notepad and assigns the process information to the $process variable. The -PassThru switch is used to return the process object, allowing you to access its properties and methods.
  2. The following lines create and update a custom performance counter:
$counter = New-Object System.Diagnostics.PerformanceCounter($categoryName, $counterName, $process.ProcessName, $false)
$counter.RawValue = 0

The New-Object cmdlet is used to create a new instance of the System.Diagnostics.PerformanceCounter class. The constructor for this class accepts four arguments:

  • $categoryName: The name of the custom performance counter category that the counter belongs to.
  • $counterName: The name of the custom performance counter.
  • $process.ProcessName: The name of the process that the custom performance counter is associated with. In this case, it is the name of the Notepad process started earlier.
  • $false: A boolean value indicating whether the counter is read-only. By setting it to $false, we are allowing the counter to be updated.

After creating the PerformanceCounter object and storing it in the $counter variable, the code sets the counter’s RawValue property to 0, initializing the counter value.

Reading custom performance counters with PowerShell

Now that we have the custom counter created, reading it is the easy part. We can read it just like we would any other performance counter! To read the value, use the Get-Counter cmdlet with the counter’s path.

Using Get-Counter cmdlet with custom counters

To read one of your new perf counter values, you need to provide the correct counter path, which includes the computer name, custom category name, process name, and counter name. It looks like this:

# Reading the custom performance counter from PowerShell

$counterPath = "\\" + $env:COMPUTERNAME + "\$categoryName($($process.Name))\$counterName"
$counterValue = Get-Counter -Counter $counterPath
$counterValue.CounterSamples.CookedValue

Making some sense of the results

This code is helpful in showing how to create a performance counter… but this code doesn’t really help us with anything. It shows how to monitor something that is set manually to the number “zero”.

Here are some more helpful things that you could monitor with the custom performance counter:

CPU usage: Monitor the percentage of CPU time consumed by the process.

$processCpuCounter = New-Object System.Diagnostics.PerformanceCounter("Process", "% Processor Time", $process.Name)
$counter.RawValue = [int]$processCpuCounter.NextValue()

Working set size (memory usage): Monitor the amount of memory used by the process.

$counter.RawValue = $process.WorkingSet64

Private bytes: Monitor the amount of private memory allocated to the process.

$privateBytesCounter = New-Object System.Diagnostics.PerformanceCounter("Process", "Private Bytes", $process.Name)
$counter.RawValue = [int]$privateBytesCounter.NextValue()

Number of threads: Monitor the number of threads used by the process.

$counter.RawValue = $process.Threads.Count

Handle count: Monitor the number of handles (file, registry, etc.) used by the process.

$counter.RawValue = $process.HandleCount

To use any of these use cases, replace the line $counter.RawValue = 0 in the original script with the corresponding code block for the desired performance metric. Note that some of these counters require a delay or a loop to get accurate values, especially the ones that use the NextValue() method.

Best practices and tips

When working with custom performance counters, keep the following best practices in mind:

  • Properly naming custom counter categories and counters: Use descriptive and unique names for your custom counter categories and counters to make them easy to identify and manage.
  • Avoiding performance overhead when using custom counters: Updating and reading custom counters can introduce some overhead. Be mindful of this when designing your monitoring strategy and avoid updating counters too frequently.
  • Ensure compatibility with monitoring tools and systems: When creating custom performance counters, try to keep them compatible with the monitoring tools and systems your organization uses.

Continuing your PowerShell journey

As you continue learning PowerShell, remember the importance of practicing and developing new skills. Consider finding a mentor, enrolling in a course, or exploring additional resources and documentation to expand your knowledge. The more you practice, the more proficient you will become in PowerShell and I swear it can change your career!

Conclusion

In this article, we have explored custom performance counters in PowerShell, which can help you monitor processes in a more tailored manner. We have covered the creation of custom performance counter categories and counters, updating and reading custom counters, and discussed some best practices for using custom performance counters. As you continue your PowerShell journey, keep experimenting and learning to get the most out of this powerful scripting language.