.Net Diagnostic Tools for Probing Your Application
Last week we explored tools like Serilog and OpenTelemetry to see what goes on in our applications. But we can only add these tools if we can modify our application. If this is not an option, we need another approach.
Thanks to the .Net diagnostics tools that Microsoft build around .Net, we can peak into our applications without modifying any code. That makes these tools a great help in any situation, even if we could change the application. Let us dive right into them.
The common part of the diagnostic tools
While the .Net diagnostic tools are a collection of different tools, they have a lot in common. We can install the diagnostic tools as a global .Net tool with this command (just replace dotnet-dump with the name of the tool you want to use):
To find the application we want to work with, we can use the option ps to list the running .Net applications:
This output shows us the two identifiers we can use with most tools: the process id and the name of the application. To use the name to identify the app we want to analyse, we can use the -n NAME option (or --name NAME):
If we use the name, we can reuse the same command even when we restart the application in between. The process Id will change whenever we start the application, but when we have two applications with the same name, we need to use the -p option with the id to tell the diagnostic tool what app we are interested in:
dotnet-counters shows performance counters
The dotnet-counters tool displays real-time performance metrics for .NET applications using the EventCounter API. It monitors CPU usage, garbage collection, exception rates, and more, making it ideal for initial health checks and quick performance investigations.
It takes a bit time to get used to the syntax to observe the right counters. Skip the general part of the documentation and dive right into the examples – that will help you better. We can get the list of the well-known counters with the list option:
To get the periodically refreshing values of the selected counters, we can use the monitor option in combination with a list of counters we are interested in:
If we want to be more specific with the counter selection, we can modify the --counters option to this:
If we want to save the result, we can use the collect option and reuse the counter selection from above:
This creates a CSV file with an entry for each counter every second. If we prefer JSON, we can set it with the --format json parameter:
dotnet-trace tracks time spend in methods
The dotnet-trace tool allows us to create a trace file that registers how long each of our methods took and how often it was called. This helps us to figure out what parts of the application are important and if they are slow or not.
We can create a trace file for our application with this command:
When the performance test run is done, we can stop recording and the output ends in a *.nettrace file.
To get a quick overview of the top 6 methods, we can use this command:
To see more details, we can open the trace file in Visual Studio or in PerfView. I find the user interface of PerfView an enormous pain and did not bother to dig deeper. If you want to learn more, you can find a good series of tutorials on learn.microsoft.com.
Visual Studio is a much nicer option to dive through the trace file. We will cover this in a dedicated post on how to find the hot path in our application.
dotnet-dump creates a memory dump
We can create a dump file containing the whole memory of our application with the dotnet-dump command:
We can use the analyze option to start digging into the dump file. Make sure that you check the documentation on the available commands. The user interface is more or less WinDbg and archaic.
A much simpler and more understandable approach is to use a tool like JetBrains dotMemory that I will cover in a few weeks in a dedicated post as well.
dotnet-gcdump dumps the heap
If we care only about the heap part of the memory, we can create the dump file with dotnet-gcdump:
We can use the report option to dive into the dump file:
This will flood your terminal with data, and it takes its time to understand the important parts of the output.
dotnet-stack shows the stack
If we are only interested in the stack part of the memory, we can use the dotnet-stack command to get the stack trace of each process inside our application:
Next
With the .Net diagnostic tools we can gain insights into our application without making any changes to it. The tools are a great help to learn more about the running application, but they are not the most user-friendly tools. Later in this series we will use Visual Studio and JetBrains dotMemory to analyse the collected data. Those tools offer a better user experience and require less knowledge to spot problems.
Next week we take a detour and explore our options to get more test data into our application – that will help us spot data related problems much faster.