Profiling .NET in Production Without Killing Performance
dotnet-trace, EventPipe, and continuous profiling with minimal overhead — our observability stack for latency investigation.
Production profiling is scary until you have the right tools. Here’s how we investigate latency without restarting services.
Continuous Profiling with dotnet-monitor
dotnet-monitor collect \
--process-id 1234 \
--duration 00:01:00 \
--profile cpu \
--output /tmp/trace.nettrace
Overhead: <1% CPU for 60-second captures. Safe for production.
Analyzing Traces
dotnet-trace report trace.nettrace \
--top-n 20 \
--inclusive \
--format json
Look for:
- GC pauses > 50ms
- Thread pool starvation (long
Waittimes) - Sync-over-async (
Task.Wait()in async code)
Our Alerting Stack
- P99 latency breach → auto-trigger 60s trace
- Trace uploaded to shared storage
- Slack notification with flame graph link
- On-call investigates within 15 minutes
Quick Wins We Found
| Issue | Impact | Fix |
|---|---|---|
| Sync JSON serialization | +40ms P99 | Source generators |
| Missing DB index | +200ms on one endpoint | Added composite index |
| Large object heap pressure | GC pauses every 30s | Object pooling for buffers |
profiling , observability , dotnet · .NET , Linux