Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article applies to: ✔️ dotnet-counters version 3.0.47001 and later versions.
Counters can be read from applications running .NET 5 or later.
Install
There are three ways to download and use dotnet-counters:
One-shot execution (recommended):
Starting with .NET 10.0.100, you can run
dotnet-counterswithout permanent installation usingdnx:dnx dotnet-counters [options]For example:
dnx dotnet-counters monitor --process-id 1234This approach automatically downloads and runs the latest version without permanently modifying your system.
dotnet global tool:
To install the latest release version of the
dotnet-countersNuGet package for frequent use, use the dotnet tool install command:dotnet tool install --global dotnet-countersThis command installs a
dotnet-countersbinary to your .NET SDK Tools path, which you can add to your PATH to easily invoke globally-installed tools.Direct download:
Download the tool executable that matches your platform:
OS Platform Windows x86 | x64 | Arm | Arm-x64 Linux x64 | Arm | Arm64 | musl-x64 | musl-Arm64
Note
To use dotnet-counters on an x86 app, you need a corresponding x86 version of the tool.
Synopsis
dotnet-counters [-h|--help] [--version] <command>
Description
dotnet-counters is a performance monitoring tool for ad-hoc health monitoring and first-level performance investigation. It can observe performance counter values that are published via the EventCounter API or the Meter API. For example, you can quickly monitor things like the CPU usage or the rate of exceptions being thrown in your .NET Core application to see if there's anything suspicious before diving into more serious performance investigation using PerfView or dotnet-trace.
Options
--versionDisplays the version of the
dotnet-countersutility.-h|--helpShows command-line help.
Commands
| Command |
|---|
| dotnet-counters collect |
| dotnet-counters monitor |
| dotnet-counters ps |
dotnet-counters collect
Periodically collect selected counter values and export them into a specified file format for post-processing.
Synopsis
dotnet-counters collect [-h|--help] [-p|--process-id] [-n|--name] [--diagnostic-port] [--refresh-interval] [--counters <COUNTERS>] [--format] [-o|--output] [-- <command>]
Options
-p|--process-id <PID>The ID of the process to collect counter data from.
Note
On Linux and macOS, using this option requires the target application and
dotnet-countersto share the sameTMPDIRenvironment variable. Otherwise, the command will time out.-n|--name <name>The name of the process to collect counter data from.
Note
On Linux and macOS, using this option requires the target application and
dotnet-countersto share the sameTMPDIRenvironment variable. Otherwise, the command will time out.--diagnostic-port <port-address[,(listen|connect)]>Sets the diagnostic port used to communicate with the process to be monitored.
dotnet-countersand the .NET runtime inside the target process must agree on the port-address, with one listening and the other connecting.dotnet-countersautomatically determines the correct port when attaching using the--process-idor--nameoptions, or when launching a process using the-- <command>option. It's usually only necessary to specify the port explicitly when waiting for a process that will start in the future or communicating to a process that's running inside a container that isn't part of the current process namespace.The
port-addressdiffers by OS:- Linux and macOS - a path to a Unix domain socket such as
/foo/tool1.socket. - Windows - a path to a named pipe such as
\\.\pipe\my_diag_port1. - Android, iOS, and tvOS - an IP:port such as
127.0.0.1:9000.
By default,
dotnet-counterslistens at the specified address. You can requestdotnet-countersto connect instead by appending,connectafter the address. For example,--diagnostic-port /foo/tool1.socket,connectwill connect to a .NET runtime process that's listening to the/foo/tool1.socketUnix domain socket.For information about how to use this option to start monitoring counters from app startup, see using diagnostic port.
- Linux and macOS - a path to a Unix domain socket such as
--refresh-interval <SECONDS>The number of seconds to delay between updating the displayed counters
--counters <COUNTERS>A comma-separated list of counters. Counters can be specified
provider_name[:counter_name]. If theprovider_nameis used without a qualifying list of counters, then all counters from the provider are shown. To discover provider and counter names, see built-in metrics. For EventCounters,provider_nameis the name of the EventSource and for Meters,provider_nameis the name of the Meter.--format <csv|json>The format to be exported. Currently available: csv, json.
-o|--output <output>The name of the output file.
-- <command>After the collection configuration parameters, the user can append
--followed by a command to start a .NET application.dotnet-counterslaunches a process with the provided command and collect the requested metrics. This is often useful to collect metrics for the application's startup path and can be used to diagnose or monitor issues that happen early before or shortly after the main entry point.Note
Using this option monitors the first .NET process that communicates back to the tool, which means if your command launches multiple .NET applications, it will only collect the first app. Therefore, it's recommended you use this option on self-contained applications, or using the
dotnet exec <app.dll>option.Note
If you launch a .NET executable via
dotnet-counters, its input/output will be redirected and you won't be able to interact with its stdin/stdout. You can exit the tool via Ctrl+C or SIGTERM to safely end both the tool and the child process. If the child process exits before the tool, the tool will exit as well. If you need to use stdin/stdout, you can use the--diagnostic-portoption. For more information, see Using diagnostic port.
Note
To collect metrics using dotnet-counters, it needs to be run as the same user as the user running target process or as root. Otherwise, the tool will fail to establish a connection with the target process.
Examples
Collect all counters at a refresh interval of 3 seconds and generate a csv as output:
> dotnet-counters collect --process-id 1902 --refresh-interval 3 --format csv --counters is unspecified. Monitoring System.Runtime counters by default. Starting a counter session. Press Q to quit.Start
dotnet mvc.dllas a child process and start collecting runtime counters and ASP.NET Core Hosting counters from startup and save it as a JSON output:> dotnet-counters collect --format json --counters System.Runtime,Microsoft.AspNetCore.Hosting -- dotnet mvc.dll Starting a counter session. Press Q to quit. File saved to counter.json
dotnet-counters monitor
Displays periodically refreshing values of selected counters.
Synopsis
dotnet-counters monitor [-h|--help] [-p|--process-id] [-n|--name] [--diagnostic-port] [--refresh-interval] [--counters] [-- <command>]
Options
-p|--process-id <PID>The ID of the process to be monitored.
-n|--name <name>The name of the process to be monitored.
--diagnostic-portThe name of the diagnostic port to create. See using diagnostic port for how to use this option to start monitoring counters from app startup.
--refresh-interval <SECONDS>The number of seconds to delay between updating the displayed counters
--counters <COUNTERS>A comma-separated list of counters. Counters can be specified
provider_name[:counter_name]. If theprovider_nameis used without a qualifying list of counters, then all counters from the provider are shown. To discover provider and counter names, see built-in metrics. For EventCounters,provider_nameis the name of the EventSource and for Meters,provider_nameis the name of the Meter.-- <command>After the collection configuration parameters, you can append
--followed by a command to start a .NET application.dotnet-counterswill launch a process with the provided command and monitor the requested metrics. This is often useful to collect metrics for the application's startup path and can be used to diagnose or monitor issues that happen early before or shortly after the main entry point.Note
Using this option monitors the first .NET process that communicates back to the tool, which means if your command launches multiple .NET applications, it will only collect the first app. Therefore, it's recommended you use this option on self-contained applications, or using the
dotnet exec <app.dll>option.Note
Launching a .NET executable via
dotnet-counterswill redirect its input/output and you won't be able to interact with its stdin/stdout. You can exit the tool via Ctrl+C or SIGTERM to safely end both the tool and the child process. If the child process exits before the tool, the tool will exit as well. If you need to use stdin/stdout, you can use the--diagnostic-portoption. For more information, see Using diagnostic port.
Note
On Linux and macOS, this command expects the target application and dotnet-counters to share the same TMPDIR environment variable.
Note
To monitor metrics using dotnet-counters, it needs to be run as the same user as the user running target process or as root.
Note
If you see an error message similar to the following one: [ERROR] System.ComponentModel.Win32Exception (299): A 32 bit processes cannot access modules of a 64 bit process., you're trying to use dotnet-counters that has mismatched bitness against the target process. Make sure to download the correct bitness of the tool in the install link.
Examples
Monitor all counters from
System.Runtimeat a refresh interval of 3 seconds:> dotnet-counters monitor --process-id 1902 --refresh-interval 3 --counters System.Runtime Press p to pause, r to resume, q to quit. Status: Running Name Current Value [System.Runtime] dotnet.assembly.count ({assembly}) 115 dotnet.gc.collections ({collection}) gc.heap.generation ------------------ gen0 5 gen1 1 gen2 1 dotnet.gc.heap.total_allocated (By) 1.6947e+08 dotnet.gc.last_collection.heap.fragmentation.size (By) gc.heap.generation ------------------ gen0 0 gen1 348,248 gen2 0 loh 32 poh 0 dotnet.gc.last_collection.heap.size (By) gc.heap.generation ------------------ gen0 0 gen1 18,010,920 gen2 5,065,600 loh 98,384 poh 3,407,048 dotnet.gc.last_collection.memory.committed_size (By) 66,842,624 dotnet.gc.pause.time (s) 0.05 dotnet.jit.compilation.time (s) 1.317 dotnet.jit.compiled_il.size (By) 574,886 dotnet.jit.compiled_methods ({method}) 6,008 dotnet.monitor.lock_contentions ({contention}) 194 dotnet.process.cpu.count ({cpu}) 16 dotnet.process.cpu.time (s) cpu.mode -------- system 4.953 user 6.266 dotnet.process.memory.working_set (By) 1.3217e+08 dotnet.thread_pool.queue.length ({work_item}) 0 dotnet.thread_pool.thread.count ({thread}) 133 dotnet.thread_pool.work_item.count ({work_item}) 71,188 dotnet.timer.count ({timer}) 124Note
If the app uses .NET version 8 or lower, the System.Runtime Meter doesn't exist in those versions and
dotnet-counterswill fall back to display the older System.Runtime EventCounters instead. The UI looks slightly different, as shown here.[System.Runtime] % Time in GC since last GC (%) 0 Allocation Rate (B / 1 sec) 5,376 CPU Usage (%) 0 Exception Count (Count / 1 sec) 0 GC Fragmentation (%) 48.467 GC Heap Size (MB) 0 Gen 0 GC Count (Count / 1 sec) 1 Gen 0 Size (B) 24 Gen 1 GC Count (Count / 1 sec) 1 Gen 1 Size (B) 24 Gen 2 GC Count (Count / 1 sec) 1 Gen 2 Size (B) 272,000 IL Bytes Jitted (B) 19,449 LOH Size (B) 19,640 Monitor Lock Contention Count (Count / 1 sec) 0 Number of Active Timers 0 Number of Assemblies Loaded 7 Number of Methods Jitted 166 POH (Pinned Object Heap) Size (B) 24 ThreadPool Completed Work Item Count (Count / 1 sec) 0 ThreadPool Queue Length 0 ThreadPool Thread Count 2 Working Set (MB) 19Monitor just garbage collections and garbage collection heap allocation from
System.Runtime:> dotnet-counters monitor --process-id 1902 --counters System.Runtime[dotnet.gc.collections,dotnet.gc.heap.total_allocated] Press p to pause, r to resume, q to quit. Status: Running Name Current Value [System.Runtime] dotnet.gc.collections ({collection}) gc.heap.generation ------------------ gen0 0 gen1 0 gen2 0 dotnet.gc.heap.total_allocated (By) 9,943,384Monitor
EventCountervalues from user-definedEventSource. For more information, see Tutorial: Measure performance using EventCounters in .NET Core.> dotnet-counters monitor --process-id 1902 --counters Samples-EventCounterDemos-Minimal Press p to pause, r to resume, q to quit. request 100Launch
my-aspnet-server.exeand monitor the # of assemblies loaded from its startup:> dotnet-counters monitor --counters System.Runtime[dotnet.assembly.count] -- my-aspnet-server.exe Press p to pause, r to resume, q to quit. Status: Running Name Current Value [System.Runtime] dotnet.assembly.count ({assembly}) 11Launch
my-aspnet-server.exewitharg1andarg2as command-line arguments and monitor its working set and GC heap size from its startup:> dotnet-counters monitor --counters System.Runtime[dotnet.process.memory.working_set,dotnet.gc.last_collection.heap.size] -- my-aspnet-server.exe arg1 arg2Name Current Value [System.Runtime] dotnet.gc.last_collection.heap.size (By) gc.heap.generation ------------------ gen0 560 gen1 462,720 gen2 0 loh 0 poh 8,184 dotnet.process.memory.working_set (By) 48,431,104
dotnet-counters ps
Lists the dotnet processes that can be monitored by dotnet-counters.
dotnet-counters version 6.0.320703 and later also displays the command-line arguments that each process was started with, if available.
Synopsis
dotnet-counters ps [-h|--help]
Example
Suppose you start a long-running app using the command dotnet run --configuration Release. In another window, you run the dotnet-counters ps command. The output you see is as follows. The command-line arguments, if any, are shown in dotnet-counters version 6.0.320703 and later.
> dotnet-counters ps
21932 dotnet C:\Program Files\dotnet\dotnet.exe run --configuration Release
36656 dotnet C:\Program Files\dotnet\dotnet.exe
Using diagnostic port
Diagnostic port is a runtime feature that allows you to start monitoring or collecting counters from app startup. To do this using dotnet-counters, you can either use dotnet-counters <collect|monitor> -- <command> as described in the previous examples, or use the --diagnostic-port option.
Using dotnet-counters <collect|monitor> -- <command> to launch the application as a child process is the simplest way to quickly monitor it from its startup.
However, when you want to gain a finer control over the lifetime of the app being monitored (for example, monitor the app for the first 10 minutes only and continue executing) or if you need to interact with the app using the CLI, using --diagnostic-port option allows you to control both the target app being monitored and dotnet-counters.
The following command makes
dotnet-counterscreate a diagnostics socket namedmyport.sockand wait for a connection.dotnet-counters collect --diagnostic-port myport.sockOutput:
Waiting for connection on myport.sock Start an application with the following environment variable: DOTNET_DiagnosticPorts=/home/user/myport.sockIn a separate console, launch the target application with the environment variable
DOTNET_DiagnosticPortsset to the value in thedotnet-countersoutput.export DOTNET_DiagnosticPorts=/home/user/myport.sock ./my-dotnet-app arg1 arg2This enables
dotnet-countersto start collecting counters onmy-dotnet-app:Waiting for connection on myport.sock Start an application with the following environment variable: DOTNET_DiagnosticPorts=myport.sock Starting a counter session. Press Q to quit.Important
Launching your app with
dotnet runcan be problematic because the dotnet CLI might spawn many child processes that aren't your app and they can connect todotnet-countersbefore your app, leaving your app to be suspended at run time. It's recommended you directly use a self-contained version of the app or usedotnet execto launch the application.