Files
natsdotnet/dottrace.md

6.3 KiB

dotTrace Command-Line Profiler

Installation

Installed as a .NET global tool:

dotnet tool install --global JetBrains.dotTrace.GlobalTools

Update to latest:

dotnet tool update --global JetBrains.dotTrace.GlobalTools

Current version: 2025.3.3

Quick Start

Profile the NATS server (sampling, 30 seconds)

dottrace start --framework=NetCore --profiling-type=Sampling \
  --timeout=30s --save-to=./snapshots/nats-sampling.dtp \
  -- dotnet run --project src/NATS.Server.Host -- -p 14222

Profile the NATS server (timeline, with async/TPL info)

dottrace start --framework=NetCore --profiling-type=Timeline \
  --timeout=30s --save-to=./snapshots/nats-timeline.dtt \
  -- dotnet run --project src/NATS.Server.Host -- -p 14222

Attach to a running server by PID

dottrace attach <PID> --profiling-type=Sampling \
  --timeout=30s --save-to=./snapshots/nats-attach.dtp

Attach by process name

dottrace attach NATS.Server.Host --profiling-type=Sampling \
  --timeout=30s --save-to=./snapshots/nats-attach.dtp

Profiling Types

Type Flag Snapshot Extension Use Case
Sampling --profiling-type=Sampling .dtp Low overhead, CPU hotspots (default)
Timeline --profiling-type=Timeline .dtt Thread activity, async/await, TPL tasks
Tracing --profiling-type=Tracing .dtp Exact call counts, higher overhead
Line-by-Line --profiling-type=LineByLine .dtp Per-line timing (not available for attach)

Sampling options

# Use thread time instead of CPU instructions
--time-measurement=ThreadTime

# Default (CPU instruction count)
--time-measurement=CpuInstruction

Timeline options

# Disable TPL data collection for better performance
--disable-tpl

Common Options

Option Description
--framework=NetCore Required for .NET Core / .NET 5+ apps
--save-to=<path> Snapshot output path (file or directory)
--overwrite Overwrite existing snapshot files
--timeout=<duration> Auto-stop after duration (e.g., 30s, 5m, 1h)
--propagate-exit-code Return the profiled app's exit code instead of dotTrace's
--profile-child Also profile child processes
--profile-child=<mask> Profile matching child processes (e.g., dotnet)
--work-dir=<path> Set working directory for the profiled app
--collect-data-from-start=off Don't collect until explicitly started via service messages

Interactive Profiling with Service Messages

For fine-grained control over when data is collected, use --service-input=stdin:

dottrace start --framework=NetCore --service-input=stdin \
  --save-to=./snapshots/nats-interactive.dtp \
  -- dotnet run --project src/NATS.Server.Host -- -p 14222

Then type these commands into stdin (each must start on a new line and end with a carriage return):

Command Effect
##dotTrace["start"] Start collecting performance data
##dotTrace["get-snapshot"] Save snapshot and stop collecting
##dotTrace["drop"] Discard collected data and stop
##dotTrace["disconnect"] Detach/stop profiler

Stdout will emit status messages like:

##dotTrace["ready"]
##dotTrace["connected", {pid: 1234, path:"dotnet"}]
##dotTrace["started", {pid: 1234, path:"dotnet"}]
##dotTrace["snapshot-saved", {pid: 1234, filename:"./snapshots/nats-interactive.dtp"}]

Example Workflows

Profile a benchmark run

dottrace start --framework=NetCore --profiling-type=Sampling \
  --save-to=./snapshots/bench.dtp \
  -- dotnet run --project tests/NATS.Server.Benchmarks -c Release

Profile tests

dottrace start --framework=NetCore --profiling-type=Sampling \
  --timeout=2m --save-to=./snapshots/tests.dtp \
  -- dotnet test tests/NATS.Server.Core.Tests --filter "FullyQualifiedName~PubSub"

Profile with child processes (e.g., server spawns workers)

dottrace start --framework=NetCore --profile-child \
  --timeout=30s --save-to=./snapshots/nats-children.dtp \
  -- dotnet run --project src/NATS.Server.Host

Exporting Reports

dotTrace's XML report tool (Reporter.exe) is Windows-only. On macOS, use dotnet-trace for profiling with exportable formats:

# Install dotnet-trace
dotnet tool install --global dotnet-trace

# Collect a trace from a running process (nettrace format)
dotnet-trace collect --process-id <PID> --duration 00:00:30

# Collect directly in speedscope format
dotnet-trace collect --process-id <PID> --format speedscope --duration 00:00:30

# Convert an existing .nettrace file to speedscope
dotnet-trace convert --format speedscope trace.nettrace

Speedscope files can be visualized at speedscope.app — a web-based flame graph viewer that works on any platform.

dotnet-trace output formats

Format Extension Viewer
nettrace (default) .nettrace PerfView, Visual Studio, Rider
speedscope .speedscope.json speedscope.app
chromium .chromium.json Chrome DevTools (chrome://tracing)

Example: profile NATS server and export flame graph

# Start the server
dotnet run --project src/NATS.Server.Host -- -p 14222 &
SERVER_PID=$!

# Collect a 30-second trace in speedscope format
dotnet-trace collect --process-id $SERVER_PID --format speedscope \
  --duration 00:00:30 --output ./snapshots/nats-trace

# Open the flame graph
open ./snapshots/nats-trace.speedscope.json  # opens in default browser at speedscope.app

Viewing Snapshots

Open .dtp / .dtt snapshot files in:

  • dotTrace GUI (/Users/dohertj2/Applications/dotTrace.app)
  • JetBrains Rider (built-in profiler viewer)
open /Users/dohertj2/Applications/dotTrace.app --args ./snapshots/nats-sampling.dtp

Exit Codes

Code Meaning
0 Success
65 Profiling failure

Notes

  • Snapshots consist of multiple files: *.dtp, *.dtp.0000, *.dtp.0001, etc. Keep them together.
  • Attach on macOS requires .NET 5 or later.
  • Use -- before the executable path if arguments start with -.
  • The snapshots/ directory is not tracked in git. Create it before profiling:
    mkdir -p snapshots