Memory.Introspect

How it works

Memory.Introspect sits on top of the .NET runtime's built-in diagnostics infrastructure. The same plumbing that powers dotnet-gcdump, dotnet-dump, and dotnet-trace is exposed here as a normal in-process API.

The .NET diagnostics pipe

Every .NET 6+ runtime exposes a diagnostics pipe at startup:

  • On Linux/macOS — a Unix domain socket in /tmp/dotnet-diagnostic-<pid>-…
  • On Windows — a named pipe \\.\pipe\dotnet-diagnostic-<pid>

The CLI diagnostic tools open that pipe, speak a small wire protocol, and instruct the runtime to do work — capture a dump, stream events, write a .gcdump, etc. Memory.Introspect does the same, but from inside (or alongside) the .NET process you care about.

EventPipe — the heap-walking channel

The .gcdump format is built from a graph of GC events. To produce one, you:

  1. Open an EventPipe session against the target process.
  2. Subscribe to GC events (GCBulkType, GCBulkNode, GCBulkEdge, …).
  3. Trigger a heap walk (Memory.Introspect does this via a GC).
  4. Stream the events out.
  5. Stitch them back together into a MemoryGraph.

CollectMemoryGraphAsync runs that pipeline and hands you the resulting graph, which you can then write to disk with SaveToDisk in the standard .gcdump file format.

CollectSamplingProfileAsync does the same dance but subscribes to CPU sampling events instead. DumpAsync is different — it uses the runtime's dump command rather than EventPipe, producing a full process dump (the equivalent of dotnet-dump collect).

Code adapted from dotnet/diagnostics

Most of the heavy lifting — protocol framing, EventPipe session management, GC event parsing, MemoryGraph construction — is taken straight from Microsoft's dotnet/diagnostics repository. The library's contribution is to package and expose that code as a clean public API:

Memory.Introspect public API
  ├─ MemoryIntrospector.Create
  ├─ CollectMemoryGraphAsync   ─►  EventPipeDotNetHeapDumper  (from dotnet-gcdump)
  ├─ CollectSamplingProfileAsync ─►  SamplingProfiler         (from dotnet-trace)
  └─ DumpAsync                  ─►  Dumper                    (from dotnet-dump)

That means:

  • The wire format and binary layout stay in lock-step with what the official tools produce.
  • .gcdump files written by SaveToDisk open in PerfView, Visual Studio, and any other .gcdump viewer without conversion.
  • When the upstream tools fix a bug or extend a protocol, the library's internals can be re-vendored from the upstream source.

Where it fits in the diagnostics workflow

Tool Captures Triggered from When to use
dotnet-gcdump CLI .gcdump Shell, by PID Ad-hoc debugging on a long-running process you can shell to.
dotnet-dump CLI Full process dump Shell, by PID Capture full memory state for offline analysis in WinDbg / dotnet-dump.
dotnet-trace CLI .nettrace (events, sampling) Shell, by PID CPU sampling, custom event sources.
Memory.Introspect All three of the above In-process or by PID Automated capture from inside an app, integration tests, CI guards.

The library doesn't replace the CLI tools — it complements them by letting you trigger the same captures from code, without spawning external processes.

Self-capture vs. cross-process capture

Both modes work. MemoryIntrospector.CollectMemoryGraphAsync(pid) accepts any PID you have permission to reach:

  • Self-capture — pass Process.GetCurrentProcess().Id. No extra privileges. Useful for "dump the heap when this metric crosses a threshold" patterns.
  • Cross-process capture — pass any PID on the box. Requires the diagnostics pipe to be openable from your process (same user on Linux/macOS; matching ACLs on Windows).

For remote scenarios — capturing from a sidecar container, for example — pass the DiagnosticPort option pointing at a named diagnostic pipe. See Configuration.

Further reading

© 2026 Memory.Introspect. All rights reserved.