Diagnostics & Performance

16 questions found

What tools does .NET provide for basic application diagnostics, and what is dotnet-trace used for?

Beginner
The .NET SDK includes global diagnostic tools installable via `dotnet tool install -g`: dotnet-trace collects CPU and runtime event traces for performance analysis, dotnet-counters displays live performance counters (GC stats, request rate, CPU usage), dotnet-dump captures and analyzes memory dumps, and dotnet-gcdump captures GC heap snapshots -- all usable against a running process without needing to restart or attach a debugger.
dotnet tool install -g dotnet-trace
dotnet-trace collect --process-id 1234 --duration 00:00:30
# Produces a .nettrace file, viewable in PerfView or Visual Studio
Real-world example An on-call engineer diagnosing a production CPU spike attaches dotnet-trace to the running process for 30 seconds without any downtime, then analyzes the resulting trace file to identify the specific method consuming excessive CPU.

Common follow-ups: How do you view a .nettrace file's contents?;What's the performance overhead of running dotnet-trace against a live process?

Logging;Memory Management & Garbage Collection

How does dotnet-counters provide live performance monitoring, and what key metrics does it expose by default?

Intermediate
dotnet-counters connects to a running .NET process and streams live performance counters to the console (or exports them), including built-in metrics like CPU usage, GC heap size and collection counts per generation, ThreadPool thread count and queue length, exception count, and (for ASP.NET Core apps) request rate and active request count -- giving a real-time operational view without needing a full profiling session.
dotnet-counters monitor --process-id 1234

# Live output includes:
# cpu-usage, gc-heap-size, gen-0-gc-count, threadpool-thread-count, request-rate...
Real-world example An engineer investigating intermittent slow responses runs dotnet-counters live during a suspected incident window, immediately spotting an unusual spike in ThreadPool queue length correlating with the reported slowness.

Common follow-ups: How do you add custom application-specific counters using EventCounter or Meter?;How does this compare to a full APM solution like Application Insights?

Logging;CLR & Runtime

How does the dotnet-gcdump tool help diagnose managed memory leaks, and how do you analyze the resulting heap snapshot?

Advanced
dotnet-gcdump collects a snapshot of the entire managed heap from a running process (triggering a full GC first to ensure only genuinely reachable objects are captured), producing a .gcdump file that can be analyzed in Visual Studio's memory analysis tools or PerfView to see object counts and retained sizes by type, and critically, trace the reference paths keeping unexpectedly numerous or large objects alive -- essential for finding the root cause of a managed memory leak (as opposed to unmanaged memory growth, which requires different tools).
dotnet tool install -g dotnet-gcdump
dotnet-gcdump collect --process-id 1234
# Analyze the resulting .gcdump in Visual Studio: Debug > Windows > Show Diagnostic Tools > Memory Usage
Real-world example A service exhibiting slow but steady memory growth over days captures two gcdump snapshots hours apart, comparing them to identify a specific cache dictionary that was never evicting old entries, growing unbounded.

Common follow-ups: What's the difference between a gcdump and a full process memory dump?;How do you identify a reference path keeping an object alive in the dump viewer?

Memory Management & Garbage Collection;Diagnostics & Performance

What is Application Insights (or OpenTelemetry) integration, and what value does it add over basic logging?

Intermediate
Application Insights (and the vendor-neutral OpenTelemetry standard it increasingly supports) provides distributed tracing (following a single logical request across multiple services), automatic dependency tracking (timing of outbound HTTP calls, database queries), live metrics, and exception tracking with full context -- going well beyond basic text-based logging by correlating related events into a coherent picture of a request's full lifecycle across a distributed system, essential for diagnosing issues in microservices architectures.
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSqlClientInstrumentation()
        .AddOtlpExporter());
Real-world example A microservices platform uses OpenTelemetry distributed tracing to follow a single user request as it flows through an API gateway, an order service, and a payment service, immediately identifying which specific downstream service was responsible for a slow response.

Common follow-ups: How does distributed tracing correlate spans across service boundaries?;What's the difference between Application Insights and the vendor-neutral OpenTelemetry approach?

Logging;Microservices & Distributed Architecture Patterns

How do EventCounters and the newer System.Diagnostics.Metrics API (Meter) let you expose custom application-specific performance metrics?

Advanced
EventCounter (older API) and Meter/Instrument (newer, OpenTelemetry-aligned API introduced in .NET 6) let application code publish custom numeric metrics (counters, gauges, histograms) beyond the built-in runtime counters -- like 'orders processed per second' or 'average payment processing latency' -- which tools like dotnet-counters or an OpenTelemetry-based observability platform can then collect and visualize alongside built-in system metrics.
private static readonly Meter _meter = new("MyApp.Orders");
private static readonly Counter<long> _ordersProcessed = _meter.CreateCounter<long>("orders.processed");
private static readonly Histogram<double> _processingDuration = _meter.CreateHistogram<double>("orders.duration");

public void ProcessOrder() {
    var sw = Stopwatch.StartNew();
    // ... processing logic ...
    _ordersProcessed.Add(1);
    _processingDuration.Record(sw.Elapsed.TotalMilliseconds);
}
Real-world example An e-commerce platform exposes a custom orders.processed counter and orders.duration histogram via the Meter API, feeding directly into their OpenTelemetry pipeline alongside built-in ASP.NET Core request metrics for a unified operational dashboard.

Common follow-ups: What's the difference between a Counter, Gauge, and Histogram instrument type?;How does dotnet-counters automatically discover custom Meters?

Logging;Microservices & Distributed Architecture Patterns

What is BenchmarkDotNet, and why is it preferred over manual Stopwatch-based timing for measuring code performance?

Intermediate
BenchmarkDotNet is a statistically rigorous micro-benchmarking library that handles JIT warm-up, runs many iterations to produce statistically meaningful results, accounts for GC behavior, and reports detailed statistics (mean, error margin, standard deviation, memory allocated) -- manual Stopwatch timing is unreliable for benchmarking because it doesn't account for JIT warm-up effects, GC pauses, or measurement noise, often producing misleading results especially for very fast operations measured in nanoseconds.
[MemoryDiagnoser]
public class StringConcatBenchmarks {
    [Benchmark]
    public string StringConcat() => "a" + "b" + "c";

    [Benchmark]
    public string StringBuilder() => new StringBuilder().Append("a").Append("b").Append("c").ToString();
}

// Run via: BenchmarkRunner.Run<StringConcatBenchmarks>();
Real-world example A team debating whether to optimize a hot string-building code path uses BenchmarkDotNet to get statistically reliable numbers showing StringBuilder is actually slower than direct concatenation for their specific small, fixed-size case, avoiding an unnecessary 'optimization' based on gut feeling.

Common follow-ups: How does BenchmarkDotNet handle JIT warm-up automatically?;What does the MemoryDiagnoser attribute add to the benchmark output?

CLR & Runtime;Memory Management & Garbage Collection

How would you diagnose a thread pool starvation issue in a production ASP.NET Core application?

Advanced
Thread pool starvation occurs when all available thread pool threads are busy (often due to blocking synchronous calls on async code, like .Result or .Wait()), causing queued work items to wait, visible via dotnet-counters' threadpool-queue-length metric climbing while threadpool-thread-count stays flat or grows slowly (since the pool only adds new threads gradually) -- diagnosed by capturing a trace or dump during the symptom window and looking for many threads blocked in synchronous wait states, then fixed by replacing blocking calls with proper async/await throughout the call chain.
// Common cause of starvation: blocking on async code
public IActionResult GetData() {
    var result = _service.GetDataAsync().Result;  // BLOCKS a thread pool thread
    return Ok(result);
}

// Fix: async all the way through
public async Task<IActionResult> GetData() {
    var result = await _service.GetDataAsync();
    return Ok(result);
}
Real-world example A production API experiencing periodic request timeouts under load is diagnosed via dotnet-counters showing threadpool-queue-length spiking to hundreds; a code audit finds a legacy synchronous .Result call blocking threads, and converting it to proper async/await resolves the starvation.

Common follow-ups: Why does the thread pool grow new threads slowly rather than immediately?;How does MinThreads configuration provide a (usually inadvisable) quick mitigation?

Concurrency (asyncio/threading/multiprocessing);Diagnostics & Performance

What is the purpose of the dotnet-monitor tool, and how does it enable diagnostics in containerized/production environments?

Intermediate
dotnet-monitor runs as a sidecar or standalone process alongside your application, exposing an HTTP API to collect diagnostics artifacts (traces, dumps, logs, metrics) on demand or automatically when trigger rules match (like high CPU or an exception occurring), without needing direct shell access to the container or manually installing diagnostic tools inside a production container -- particularly valuable in Kubernetes environments where interactive debugging access is often restricted.
# Running as a sidecar in Kubernetes, collecting a trace via its HTTP API:
curl -X POST "http://localhost:52323/trace?pid=1&durationSeconds=30" -o trace.nettrace

# Or configured to auto-collect a dump when CPU exceeds a threshold for 30 seconds
{ "CollectionRuleDefaults": { "Triggers": { "CPUUsage": { "GreaterThan": 90, "SlidingWindowDuration": "00:00:30" } } } }
Real-world example A Kubernetes-hosted microservices platform deploys dotnet-monitor as a sidecar in every pod, allowing on-call engineers to remotely capture a memory dump from a misbehaving production pod via a simple HTTP call, without needing kubectl exec shell access.

Common follow-ups: How do collection rules automate diagnostics without human intervention?;What security considerations apply to exposing dotnet-monitor's API?

Docker & Containerization;Health Checks & Readiness/Liveness Probes

How does async/await performance compare to synchronous code, and what overhead does the async state machine introduce?

Advanced
Each async method is compiled into a state machine (a class or struct implementing IAsyncStateMachine) that captures local variables and tracks execution position across await points, introducing some overhead (allocation for the state machine if it can't be a struct, plus continuation scheduling) compared to purely synchronous code -- but this overhead is typically negligible compared to the benefit of not blocking a thread during genuinely asynchronous I/O operations (network calls, file access, database queries), where the alternative (blocking a thread pool thread) would be far more costly at scale under concurrent load.
// Async state machine overhead is real but usually negligible relative to the I/O wait time
public async Task<Order> GetOrderAsync(int id) {
    return await _db.Orders.FindAsync(id);  // thread NOT blocked during the DB roundtrip
}

// vs blocking equivalent (bad under load):
public Order GetOrder(int id) {
    return _db.Orders.Find(id);  // blocks a thread pool thread for the entire DB roundtrip
}
Real-world example A benchmark comparing sync and async versions of a database-heavy endpoint under load shows the async version sustaining several times higher throughput under concurrency, since it doesn't tie up thread pool threads waiting on I/O, despite each individual async call having marginally higher per-call overhead.

Common follow-ups: When does the state machine's overhead actually matter (very hot, tight loops with no real I/O)?;How does ValueTask reduce allocation overhead for frequently-synchronously-completing async methods?

Concurrency (asyncio/threading/multiprocessing);Memory Management & Garbage Collection

What is a memory dump, and how do you capture and analyze one for a .NET application experiencing high memory usage or a crash?

Intermediate
A memory dump is a complete snapshot of a process's memory at a point in time, capturable via dotnet-dump collect (for live processes) or automatically on crash (via configuration), then analyzed using dotnet-dump analyze's SOS debugger commands (like `dumpheap -stat` to see object counts by type) or loaded into Visual Studio/WinDbg for deeper investigation -- essential for post-mortem analysis of crashes or diagnosing memory issues that are hard to reproduce outside production.
dotnet tool install -g dotnet-dump
dotnet-dump collect --process-id 1234
dotnet-dump analyze core_20240115.dmp

# Inside the analyze REPL:
> dumpheap -stat
# Shows object counts and total size by type, sorted by total size
Real-world example A production crash investigation captures a dump automatically via a crash handler configuration, and a subsequent dotnet-dump analyze session reveals an unexpectedly massive number of retained HttpClient instances, pointing to a resource leak from not reusing HttpClient properly.

Common follow-ups: How do you configure automatic dump collection on unhandled exceptions or crashes?;What SOS commands help trace why a specific object is still referenced?

Memory Management & Garbage Collection;Global Exception Handling & Middleware

Showing 1–10 of 16