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:
- Open an EventPipe session against the target process.
- Subscribe to GC events (
GCBulkType,GCBulkNode,GCBulkEdge, …). - Trigger a heap walk (Memory.Introspect does this via a GC).
- Stream the events out.
- 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.
.gcdumpfiles written bySaveToDiskopen in PerfView, Visual Studio, and any other.gcdumpviewer 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
dotnet/diagnostics— the upstream repository.- Microsoft docs — Diagnose memory leaks — the canonical workflow.
- EventPipe documentation — protocol and event sources.