1. What is dotnet-trace?
dotnet-trace is a .NET Core/ .NET CLI diagnostic tool that collects runtime traces from your app using EventPipe.
Think of it as:
- ✅ A lightweight profiler you can run in production (no restart needed)
- ✅ A way to capture CPU, GC, exceptions, thread activity, etc.
- ✅ A tool that generates
.nettracefiles you can open in PerfView, Visual Studio, or convert to speedscope for flame charts.
It works for:
- .NET Core 3.0+
- .NET 5/6/7/8/9 apps
On Windows / Linux / macOS.
2. When to use dotnet-trace (Use Cases)
Use dotnet-trace whenever you need to understand what your .NET process is doing internally:
🔹 Common scenarios
- High CPU investigation
- CPU 80–90% on production app
- You want to know which methods are consuming CPU.
- GC / Memory issues
- Frequent GC
- High allocation rate
- Pauses impacting latency.
- Random hangs / delays
- Requests are slow, but traditional logging doesn’t tell you why.
- Startup performance issues
- App is slow to start; you want to see what happens during startup.
- Comparing before vs after optimizations
- Capture a trace before tuning,
- Capture another trace after changes,
- Compare CPU/GC patterns.
- Lab / Training
- Teaching .NET runtime internals
- Showing real-world behavior of GC, threads, etc.
3. Where to use dotnet-trace
You can use it on:
- Local developer machine – debugging / demos
- Staging / UAT – reproduce production-like load and trace
- Production – short, targeted traces when you hit an incident
- It’s sampling-based (for CPU) and event-based, so usually safe if you keep duration limited.
Works with:
- Kestrel apps (ASP.NET Core)
- Worker services, console apps
- Windows services, Linux systemd services
- Apps running in Docker / Kubernetes (attach using PID inside container).
4. How to install dotnet-trace
You already ran this, but let’s document it properly.
4.1 Prerequisites
- .NET SDK installed (you already have
dotnet --versionworking).
4.2 Install as a global tool
dotnet tool install --global dotnet-trace
Code language: PHP (php)
If already installed and you want to update:
dotnet tool update --global dotnet-trace
Code language: PHP (php)
Verify:
dotnet-trace --version
dotnet-trace --help
5. Key Concepts (Before Commands)
A few basics:
- Process ID (PID) – you attach to a running process using
--process-id <pid>. - EventPipe – internal mechanism used to stream runtime events.
- Profiles – preconfigured sets of providers/events:
cpu-samplinggc-collectgc-verbose
- .nettrace file – the output that you later inspect or convert.
6. Basic Commands (dotnet-trace CLI)
6.1 List processes you can trace
dotnet-trace ps
Typical output:
12345 Orders.Api
30280 Payments.Worker
Code language: CSS (css)
Use the PID in collect.
6.2 Collect a basic trace from a running app
dotnet-trace collect --process-id 30280
Default output (e.g., trace.nettrace) will be generated in the current directory.
Press Ctrl + C to stop collection.
6.3 Save trace with custom filename
dotnet-trace collect
--process-id 30280
--output C:tracesordersapi_2026-11-25.nettrace
Code language: CSS (css)
6.4 Use a profile (recommended)
CPU-focused trace:
dotnet-trace collect
--process-id 30280
--profile cpu-sampling
--duration 00:02:00
Code language: CSS (css)
This:
- Focuses on CPU events
- Samples for 2 minutes
GC-focused trace:
dotnet-trace collect
--process-id 30280
--profile gc-collect
--duration 00:01:00
Code language: CSS (css)
6.5 Start a process and trace it from the beginning
dotnet-trace collect -- dotnet Orders.Api.dll
Code language: CSS (css)
--separates dotnet-trace arguments from the child process command.- dotnet-trace will exit with the app’s exit code.
6.6 List available profiles
dotnet-trace list-profiles
Code language: PHP (php)
You’ll see built-in profiles with descriptions.
6.7 Convert traces to another format (for flame charts)
Convert .nettrace → speedscope:
dotnet-trace convert trace.nettrace --format speedscope
Code language: CSS (css)
This produces something like:
trace.speedscope.json
Code language: CSS (css)
You can open that on speedscope.app by uploading the JSON file to visualize CPU flame charts.
6.8 Generate textual reports
dotnet-trace report trace.nettrace --report-type cpu
Code language: CSS (css)
Common report types:
cpugc-collectgc-verbose
Example:
dotnet-trace report trace.nettrace --report-type gc-verbose
Code language: CSS (css)
7. Step-by-Step: End-to-End Workflow (Basic)
Let’s build a simple “incident to analysis” flow.
Scenario: High CPU on Orders.Api
- Identify PID
dotnet-trace psAssume you see:30280 Orders.Api - Collect CPU trace for 2 minutes
dotnet-trace collect --process-id 30280 --profile cpu-sampling --duration 00:02:00 --output C:tracesorders_highcpu.nettrace - Convert to speedscope & analyze flame chart
dotnet-trace convert C:tracesorders_highcpu.nettrace --format speedscopeThis generates a.speedscope.json.
Then:- Open browser → speedscope.app
- Upload the file
- Look at:
- Top hot methods
- Call stacks leading to those methods
- Identify which controller / EF query / third-party library eats CPU.
- Optional: Generate CPU text report
dotnet-trace report C:tracesorders_highcpu.nettrace --report-type cpuYou get a summary of top CPU-consuming call stacks in text form. - Apply Fix → Re-run trace
- Optimize that method / query
- Repeat trace to verify improvement.
8. Step-by-Step: GC / Memory Analysis Workflow
Scenario: GC is frequently running and latency increases.
- Capture GC-focused trace
dotnet-trace collect --process-id 30280 --profile gc-verbose --duration 00:01:30 --output C:tracesorders_gcverbose.nettrace - Generate GC report
dotnet-trace report C:tracesorders_gcverbose.nettrace --report-type gc-verbose - Understand the output (key elements)
Typical info:
- Number of Gen 0 / Gen 1 / Gen 2 collections
- LOH (Large Object Heap) allocation / collections
- Pause times
- Allocation patterns per thread or per call stack.
Things to look for:
- Very frequent Gen 2 collections → large survival → potential memory pressure
- Large LOH activity → big arrays / big strings (e.g., big JSON / images)
- Which methods are allocating heavily.
- Fix candidates:
- Reduce allocations in hot loops
- Pool reusable objects
- Avoid large temporary lists/arrays
- Fix unbounded caching.
9. How to Understand dotnet-trace Results (Conceptually)
9.1 CPU Sampling
From CPU report and flame charts, you’ll see:
- Which methods appear on top (most time spent)
- Call chain: e.g.,
Controller → Service → Repository → EF Core → SQL
Interpretation:
- The width (in flame chart) or top % (in report) = how expensive it is.
- If business logic / EF query dominates:
- Optimize queries, add indexes, reduce synchronous blocking, etc.
9.2 GC/Memory
From GC reports:
- High number of collections in short duration → heavy allocations
- Long pause times → GC pauses affecting latency
- Many Gen 2 collections → objects living too long / large memory usage
You correlate:
- When GC happens vs request latency
- Allocation-heavy methods → fix them.
9.3 Using PerfView or Visual Studio
Although dotnet-trace is a CLI collector, the .nettrace file can be:
- Opened in PerfView
- Imported into Visual Studio Performance Profiler (depending on version)
In those tools, you get:
- Graphs for CPU over time
- GC, threads, exceptions
- Call tree, hot paths, etc.
10. Advanced Usage
10.1 Combining with Kubernetes / Docker
Inside a container:
- Exec into the pod:
kubectl exec -it orders-api-pod-xyz -- /bin/bash - Install dotnet-trace in the container (or via a base image).
- List processes:
dotnet-trace ps - Collect from the .NET process inside the container.
You can then copy the .nettrace file out using:
kubectl cp <namespace>/<pod>:/path/inside/container/trace.nettrace ./trace.nettrace
Code language: HTML, XML (xml)
10.2 Filtering Providers (Custom EventPipe configs)
For very advanced scenarios, you can specify providers manually with --providers, but most of the time, profiles are enough. If you want ultra-deep control:
- Custom providers (e.g.,
Microsoft-AspNetCore-Server-Kestrel,System.Net.Http) - Specific keywords and levels for tracing.
Example (indicative):
dotnet-trace collect
--process-id 30280
--providers "Microsoft-AspNetCore-Server-Kestrel:0xFFFFFFFF:4"
Code language: CSS (css)
(You’ll usually only do this when you exactly know which ETW/EventSource providers you want.)
10.3 Automating in a Script
You can create a script, e.g., capture_cpu_trace.ps1:
$pid = (dotnet-trace ps | Select-String "Orders.Api").ToString().Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[0]
dotnet-trace collect `
--process-id $pid `
--profile cpu-sampling `
--duration 00:02:00 `
--output "C:tracesorders_cpu_$((Get-Date).ToString('yyyyMMdd_HHmmss')).nettrace"
Code language: JavaScript (javascript)
This lets you quickly capture CPU traces without manually checking PID each time.
11. Advantages of dotnet-trace
✅ 1. No code changes required
You attach to running processes. No need to modify source/logging.
✅ 2. Low overhead (safe for short runs in production)
Designed for live diagnostics with reasonable performance overhead when used carefully.
✅ 3. Works everywhere .NET runs
Windows, Linux, macOS, Docker, K8s.
✅ 4. Deep runtime insights
CPU, GC, threads, allocations, exceptions, etc., from the runtime level.
✅ 5. Integrates with other tools
- PerfView
- Visual Studio profiler
- speedscope (flame charts)
- Other trace analysis tools that support
.nettraceor converted formats.
✅ 6. Perfect for training / labs
You can demonstrate effect of code changes, GC settings, async vs sync, etc., visually.
12. Quick dotnet-trace Cheatsheet (Summary)
# List .NET processes
dotnet-trace ps
# Collect basic trace
dotnet-trace collect --process-id <pid>
# Collect CPU trace for 2 min
dotnet-trace collect --process-id <pid> --profile cpu-sampling --duration 00:02:00 --output cpu.nettrace
# Collect GC-verbose trace
dotnet-trace collect --process-id <pid> --profile gc-verbose --duration 00:01:00 --output gc.nettrace
# Run an app and trace it
dotnet-trace collect -- dotnet MyApp.dll
# List built-in profiles
dotnet-trace list-profiles
# Convert to speedscope
dotnet-trace convert cpu.nettrace --format speedscope
# CPU report
dotnet-trace report cpu.nettrace --report-type cpu
# GC-verbose report
dotnet-trace report gc.nettrace --report-type gc-verbose
Code language: CSS (css)
I’m a DevOps/SRE/DevSecOps/Cloud Expert passionate about sharing knowledge and experiences. I have worked at Cotocus. I share tech blog at DevOps School, travel stories at Holiday Landmark, stock market tips at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow , and SEO strategies at Wizbrand.
Do you want to learn Quantum Computing?
Please find my social handles as below;
Rajesh Kumar Personal Website
Rajesh Kumar at YOUTUBE
Rajesh Kumar at INSTAGRAM
Rajesh Kumar at X
Rajesh Kumar at FACEBOOK
Rajesh Kumar at LINKEDIN
Rajesh Kumar at WIZBRAND
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals
This tutorial offers a thorough and practical guide on using dotnet-trace, a powerful yet lightweight tool for tracing .NET applications at runtime. The article explains how to capture essential performance data like CPU usage, garbage collection, and threading without interrupting your live application. It provides clear instructions on how to collect traces, analyze the data, and identify performance bottlenecks. This tool is invaluable for developers working in production environments, offering insights into application behavior with minimal overhead. The step-by-step approach makes it accessible for both beginners and experienced developers looking to troubleshoot and optimize their .NET applications efficiently.