Attaching a debugger to a production process is rarely an option, and "add more logging, redeploy, wait for it to happen again" is a slow way to chase an intermittent issue. The dotnet-trace, dotnet-counters, and dotnet-dump global tools solve this — they attach to a running process, capture what you need, and detach, with no code changes and no restart.

Key takeaways
  • All three tools attach to an already-running process by PID — no code changes, no redeploy, no restart required.
  • dotnet-counters gives you a live view of GC, thread pool, and request metrics; start there for “something's wrong but I don't know what.”
  • dotnet-trace captures a CPU/event trace you can analyze offline in Speedscope or PerfView; use it once counters point at a specific symptom.
  • dotnet-dump captures a full memory snapshot for offline analysis — the right tool for memory leaks and hangs, not CPU issues.

Installing the tools

bash
dotnet tool install --global dotnet-trace
dotnet tool install --global dotnet-counters
dotnet tool install --global dotnet-dump

# find the process you want to inspect
dotnet-counters ps

In a container, install these into the image (or a debug sidecar) ahead of time — you don't want the first attempt at installing a diagnostic tool to happen during an active incident.

dotnet-counters: start here

When something's slow or using too much memory with no specific lead yet, dotnet-counters gives you a live terminal dashboard of GC generation counts, heap size, thread pool queue length, and exception rate — usually enough to point you at GC pressure, thread pool starvation, or neither within a couple of minutes.

bash
dotnet-counters monitor --process-id 4821 System.Runtime

You're not limited to the built-in runtime counters. The System.Diagnostics.Metrics.Meter API lets you publish your own application-specific counters — queue depth, cache hit rate, orders processed per minute — and dotnet-counters will pick them up alongside the runtime metrics with zero extra tooling.

csharp
private static readonly Meter Meter = new("Astrivo.OrderProcessing");
private static readonly Counter<long> OrdersProcessed =
    Meter.CreateCounter<long>("orders.processed");

public void Process(Order order)
{
    OrdersProcessed.Add(1, new KeyValuePair<string, object?>("status", order.Status.ToString()));
}

dotnet-trace: when counters point at CPU

Once counters tell you CPU or a specific code path is the problem, capture a trace to see exactly where time is going.

bash
dotnet-trace collect --process-id 4821 --duration 00:00:30

# open the resulting .nettrace file at speedscope.app,
# or analyze it directly with PerfView on Windows

A 30-second trace under real load is usually enough to spot a hot method that's disproportionately expensive, or confirm that time is going somewhere unexpected — serialization, a regex compiled per-request instead of cached, an accidental synchronous call in an async path.

dotnet-dump: memory leaks and hangs

For a slow memory leak or a process that's stopped responding, a live memory snapshot beats guessing.

bash
dotnet-dump collect --process-id 4821

dotnet-dump analyze core_20260714_091500
> dumpheap -stat        # what's on the heap, sorted by size
> dumpheap -mt <addr>   # every instance of a specific type
> gcroot <addr>         # why is this object still referenced?

gcroot is the command that actually answers the question that matters — not just what's using memory but what's holding a reference to it and refusing to let it go. A static event handler that never unsubscribes, or a cache with no eviction policy, are the usual suspects it turns up.

Safe in productionAll three tools have low enough overhead to run against a live production process without materially affecting it. That's the entire point — they exist specifically so you don't have to choose between diagnosing an issue and keeping the service up.

How this fits with OpenTelemetry

If you've already wired up distributed tracing with OpenTelemetry, these tools aren't a replacement — they're what you reach for when the exported traces and metrics tell you which service is misbehaving, and you need to go one level deeper into why, on that specific process, right now.

Need help with this in production?

astrivo helps teams design, build, and modernize systems like this. Let's talk about your context.