Install globally:
dotnet tool install -g asynkron-profiler --prereleaseA lightweight CLI for CPU, memory allocation, exception, lock contention, and heap profiling of any .NET command using dotnet-trace and dotnet-gcdump.
This is a profiler frontend/CLI UI built for humans and automation (including AI agents). It outputs plain‑text, structured summaries so the same insights you’d inspect in GUI tools like dotMemory or dotTrace are available to scripts and agent workflows.
- .NET SDK 10.x
dotnet-traceanddotnet-gcdumpinstalled:
dotnet tool install -g dotnet-trace
dotnet tool install -g dotnet-gcdumpBuild your app in Release, then point the profiler at the compiled output (fastest and avoids profiling the build itself):
dotnet build -c Release
asynkron-profiler --cpu -- ./bin/Release/<tfm>/MyAppFramework-dependent apps can be profiled via dotnet:
asynkron-profiler --cpu -- dotnet ./bin/Release/<tfm>/MyApp.dllYou can also pass a project or solution file directly. The profiler will build Release and run the resulting executable:
asynkron-profiler --cpu -- ./MyApp.csproj
asynkron-profiler --cpu -- ./MySolution.slnYou can also profile dotnet run, but it will mostly capture the dotnet host (and build/restore). Use this only if you can't run the built output directly:
asynkron-profiler --cpu -- dotnet run -c Release ./MyApp.csprojdotnet build -c Releasedotnet pack -c Release -o ./nupkg src/ProfileTool/ProfileTool.csprojInstall from the local package:
dotnet tool install -g --add-source ./nupkg asynkron-profilerCPU call tree:
Memory allocation call tree:
CPU profile a command:
asynkron-profiler --cpu -- ./bin/Release/<tfm>/MyAppMemory allocation profile (GC allocation ticks + call tree):
asynkron-profiler --memory -- ./bin/Release/<tfm>/MyAppException profile:
asynkron-profiler --exception -- ./bin/Release/<tfm>/MyAppLock contention profile:
asynkron-profiler --contention -- ./bin/Release/<tfm>/MyAppHeap snapshot:
asynkron-profiler --heap -- ./bin/Release/<tfm>/MyAppYou can render existing files without re-running the app. Supported inputs:
- CPU:
.speedscope.jsonor.nettrace(will convert to Speedscope) - Memory:
.nettraceor.etlx - Exceptions:
.nettraceor.etlx - Contention:
.nettraceor.etlx - Heap:
.gcdump(or adotnet-gcdump reporttext file)
Examples:
# Auto-selects CPU/memory/contention/heap based on file extension
asynkron-profiler --input /path/to/trace.nettrace
# Force CPU rendering for a speedscope file
asynkron-profiler --input /path/to/trace.speedscope.json --cpu
# Render memory allocations from an .etlx
asynkron-profiler --input /path/to/trace.etlx --memory
# Render contention from an .etlx
asynkron-profiler --input /path/to/trace.etlx --contention
# Render exceptions from an .etlx
asynkron-profiler --input /path/to/trace.etlx --exception
# Render heap dump
asynkron-profiler --input /path/to/heap.gcdump --heap
# Manual flow: collect a CPU trace, then render it
dotnet-trace collect --output ./profile-output/app.nettrace -- dotnet run MyProject.sln
asynkron-profiler --input ./profile-output/app.nettrace --cpuOutputs are written to profile-output/ in the current working directory.
--cpuCPU profile only--memorymemory allocation profile only--exceptionexception profile only--contentionlock contention profile only--heapheap snapshot only--root <text>root the call tree at the first matching method--calltree-depth <n>max call tree depth (default: 30)--calltree-width <n>max children per node (default: 4)--calltree-selfinclude self-time tree--calltree-sibling-cutoff <n>hide siblings below X% of the top sibling (default: 5)--filter <text>filter function tables by substring--exception-type <text>filter exception tables and call trees by exception type--include-runtimeinclude runtime/process frames--input <path>render existingnettrace,speedscope.json,etlx, orgcdumpfiles--tfm <tfm>target framework when profiling a.csprojor.sln
Sampled CPU profiling with a call tree and top‑function table. Use this to find the hottest methods by time.
Example (examples/cpu):
dotnet build -c Release examples/cpu/CpuDemo.csproj
asynkron-profiler --cpu -- ./examples/cpu/bin/Release/net10.0/CpuDemoSample output (top of call tree):
Call Tree (Total Time)
327.41 ms 100.0% 1x Total
└─ 299.66 ms 91.5% 1x Program.Main
└─ 298.22 ms 91.1% 1x Program.RunWork
└─ 281.02 ms 85.8% 1x Program.CpuWork
└─ 276.10 ms 84.3% 3x Program.Fib
Allocation profiling using GC allocation tick events, plus a per‑type call tree. Use this to find the biggest allocation sources.
Example (examples/memory):
dotnet build -c Release examples/memory/MemoryDemo.csproj
asynkron-profiler --memory -- ./examples/memory/bin/Release/net10.0/MemoryDemoSample output (top of call tree):
Allocation Call Tree (Sampled)
Byte[] (20.06 MB, 69.4%, 198x)
20.06 MB 100.0% 198x Byte[]
└─ 19.95 MB 99.5% 197x Program.Main lambda
Exception profiling with thrown counts, throw‑site call tree, and optional catch‑site table/tree.
Use --exception-type to focus on a specific exception.
Example (examples/exception):
dotnet build -c Release examples/exception/ExceptionDemo.csproj
asynkron-profiler --exception --exception-type "InvalidOperation" -- ./examples/exception/bin/Release/net10.0/ExceptionDemoSample output (top of call tree):
Call Tree (Thrown Exceptions)
6,667x 100.0% InvalidOperationException
└─ 6,667x 100.0% Program.Main lambda
└─ 6,667x 100.0% EH.DispatchEx
Lock contention profiling with wait‑time call tree and top contended methods. Use this to find where threads are blocking on locks.
Example (examples/contention):
dotnet build -c Release examples/contention/ContentionDemo.csproj
asynkron-profiler --contention -- ./examples/contention/bin/Release/net10.0/ContentionDemoSample output (top of call tree):
Call Tree (Wait Time)
49563.89 ms 100.0% 96x Total
└─ 49563.89 ms 100.0% 96x Program.RunWorkers lambda
└─ 49563.89 ms 100.0% 96x Program.WorkWithLock
Heap snapshot using dotnet-gcdump, with a summary of top types.
Use this to inspect retained memory after the run.
Example (examples/heap):
dotnet build -c Release examples/heap/HeapDemo.csproj
asynkron-profiler --heap -- ./examples/heap/bin/Release/net8.0/HeapDemoSample output (top types):
HEAP SNAPSHOT: HeapDemo
14,595,494 GC Heap bytes
50,375 GC Heap objects
Object Bytes Count Type
524,312 1 System.Byte[][] (Bytes > 100K)
30,168 1 System.String (Bytes > 10K)
24 50,003 System.Byte[]
- .NET SDK 10.x installed (
dotnet --list-sdks). - Global tools installed and on
PATH:asynkron-profiler,dotnet-trace,dotnet-gcdump.- Default tool paths:
~/.dotnet/tools(macOS/Linux) or%USERPROFILE%\\.dotnet\\tools(Windows).
- Default tool paths:
- Prefer running the built output (Release) instead of
dotnet runto avoid build/restore noise.
dotnet-tracenot found: install withdotnet tool install -g dotnet-trace, then ensure the global tool path is onPATH.dotnet-gcdumpnot found: install withdotnet tool install -g dotnet-gcdump.Failed to create sessionorDiagnostics IPC error: the target process must allow diagnostics. Ensure it is not started withDOTNET_EnableDiagnostics=0orCOMPlus_EnableDiagnostics=0and run as the same user.- No data or empty allocation tables: run with
--memory(GC allocation ticks) or provide a.nettrace/.etlxthat includes GC allocation events. - Slow or huge traces: reduce workload/iterations or add app-side filters, then re-run.
CPU profiling:
asynkron-profiler --cpu -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --cpu --calltree-depth 5 -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --cpu --input ./profile-output/app.speedscope.json
Memory profiling:
asynkron-profiler --memory -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --memory --root "MyNamespace" -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --memory --input ./profile-output/app.nettrace
Exception profiling:
asynkron-profiler --exception -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --exception --exception-type "InvalidOperation" -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --exception --input ./profile-output/app.nettrace
Contention profiling:
asynkron-profiler --contention -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --contention --calltree-depth 5 -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --contention --input ./profile-output/app.nettrace
Heap snapshot:
asynkron-profiler --heap -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --heap --input ./profile-output/app.gcdump
Render existing traces:
asynkron-profiler --input ./profile-output/app.nettrace
asynkron-profiler --input ./profile-output/app.speedscope.json --cpu
asynkron-profiler --input ./profile-output/app.etlx --memory
asynkron-profiler --input ./profile-output/app.etlx --contention
asynkron-profiler --input ./profile-output/app.etlx --exception
General:
asynkron-profiler --help
asynkron-profiler --cpu --calltree-depth 20 -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --memory --root "MyNamespace" -- ./bin/Release/<tfm>/MyApp
asynkron-profiler --input ./profile-output/app.nettrace --cpu

