Documentation
Metreja · A .NET profiler built for AI coding agents
Overview
Metreja is a CLI-first .NET profiler designed to be driven by AI coding agents. Every operation is a command. Every output is machine-readable NDJSON. The full measure-analyze-fix loop runs without human intervention.
It consists of two components: a native C++ profiler DLL that attaches to the .NET runtime via COR_PROFILER environment variables, and a C# CLI tool for session management and trace analysis.
| Command | What it does |
|---|---|
| metreja hotspots | Rank methods by self time, inclusive time, call count, or allocations |
| metreja calltree | Expand a slow method into its full call tree with timing at every level |
| metreja callers | Find who calls a method and how much time each caller contributes |
| metreja memory | GC counts by generation, pause times, per-type allocation hotspots |
| metreja analyze-diff | Compare two traces to verify a fix actually improved performance |
| metreja flush | Trigger an on-demand stats flush on a running profiled process |
Installation
Prerequisites: .NET 8 SDK or later, Windows 10/11 (x64).
dotnet tool install -g Metreja.ToolAfter installation, the metreja command is available globally in your terminal.
Quick Start
Five commands from zero to actionable hotspot data:
# 1. Create a session
SESSION=$(metreja init --scenario "baseline")
# 2. Tell it what to trace (your code, not the framework)
metreja add include -s $SESSION --assembly MyApp
metreja add exclude -s $SESSION --assembly "System.*"
metreja add exclude -s $SESSION --assembly "Microsoft.*"
# 3. Generate the profiler environment and run your app
metreja generate-env -s $SESSION --format shell > env.sh
source env.sh && dotnet run --project src/MyApp -c Release
# 4. Find the bottleneck
metreja hotspots .metreja/output/*.ndjson --top 10
# 5. Drill into the slowest method
metreja calltree .metreja/output/*.ndjson --method "ValidateInventory"Commands
Analysis commands read NDJSON trace files produced by the profiler. Pass a file path or a glob pattern as the first argument.
hotspots
Per-method timing ranked by self time, inclusive time, call count, or allocation count.
| Option | Type | Default | Description |
|---|---|---|---|
| file | string | — | Required. NDJSON trace file |
| --top | int | 20 | Number of methods to show |
| --min-ms | double | 0.0 | Minimum time threshold (ms) |
| --sort | string | self | self, inclusive, calls, or allocs |
| --filter | string[] | — | Filter by method/class/namespace pattern |
metreja hotspots trace.ndjson --top 10 --sort self
metreja hotspots trace.ndjson --filter "MyApp.Services" --min-ms 1calltree
Call tree for a specific method invocation, slowest first.
| Option | Type | Default | Description |
|---|---|---|---|
| file | string | — | Required. NDJSON trace file |
| --method | string | — | Required. Method name or pattern |
| --tid | long | — | Filter by thread ID |
| --occurrence | int | 1 | Which invocation (1 = slowest) |
metreja calltree trace.ndjson --method "ProcessOrder"
metreja calltree trace.ndjson --method "ProcessOrder" --occurrence 2callers
Who calls a method, with call count and timing per caller.
| Option | Type | Default | Description |
|---|---|---|---|
| file | string | — | Required. NDJSON trace file |
| --method | string | — | Required. Method name or pattern |
| --top | int | 20 | Number of callers to show |
metreja callers trace.ndjson --method "SaveChanges" --top 10memory
GC summary (generation counts, pause times) and per-type allocation hotspots.
| Option | Type | Default | Description |
|---|---|---|---|
| file | string | — | Required. NDJSON trace file |
| --top | int | 20 | Number of allocation types |
| --filter | string[] | — | Filter by class name pattern |
metreja memory trace.ndjson --top 20
metreja memory trace.ndjson --filter "System.String"analyze-diff
Compare two traces. Shows per-method timing delta (base vs. compare).
| Argument | Type | Description |
|---|---|---|
| base | string | Required. Base NDJSON file |
| compare | string | Required. Comparison NDJSON file |
metreja analyze-diff baseline.ndjson optimized.ndjsonreport
Report an issue to the GitHub repository. Requires the GitHub CLI (gh) installed and authenticated.
| Option | Type | Description |
|---|---|---|
| -t, --title | string | Required. Issue title |
| -d, --description | string | Required. Issue body/description |
metreja report --title "Bug: crash on empty trace" --description "Detailed description."Exit codes: 0 success · 1 failed · 2 gh not installed · 3 gh not authenticated
Session Management
Sessions store profiler configuration in .metreja/sessions/{sessionId}.json. Each session has a unique 6-hex-char ID.
init
Create a new profiling session. Returns a random session ID.
| Option | Type | Default | Description |
|---|---|---|---|
| --scenario | string | — | Optional scenario name |
metreja init
metreja init --scenario "before-refactor"add include / add exclude
Add filter rules controlling which methods get traced. Supports wildcards (*).
| Option | Type | Default | Description |
|---|---|---|---|
| -s, --session | string | — | Required. Session ID |
| --assembly | string[] | * | Assembly name pattern |
| --namespace | string[] | * | Namespace pattern |
| --class | string[] | * | Class name pattern |
| --method | string[] | * | Method name pattern |
metreja add include -s a1b2c3 --assembly MyApp
metreja add include -s a1b2c3 --assembly MyApp --namespace "MyApp.Core"
metreja add exclude -s a1b2c3 --assembly "System.*"
metreja add exclude -s a1b2c3 --class "*Generated*"generate-env
Generate a script that sets the environment variables to attach the profiler to your app.
| Option | Type | Default | Description |
|---|---|---|---|
| -s, --session | string | — | Required. Session ID |
| --format | string | batch | batch, powershell, or shell |
| --force | bool | false | Generate even if profiler DLL is not found |
metreja generate-env -s a1b2c3 # Windows batch
metreja generate-env -s a1b2c3 --format powershell # PowerShell
metreja generate-env -s a1b2c3 --format shell # bash/shset
Configure session settings via subcommands.
| Subcommand | Arguments | Description |
|---|---|---|
| set metadata | -s ID "scenario-name" | Update scenario name |
| set output | -s ID "path/pattern.ndjson" | Set output path (supports {sessionId}, {pid} tokens) |
| set max-events | -s ID 50000 | Cap event count (0 = unlimited) |
| set compute-deltas | -s ID true | Enable delta timing for performance analysis |
validate
Check session configuration for errors. Exits with code 1 on failure.
metreja validate -s a1b2c3flush
Trigger an on-demand stats flush on a running profiled process. Useful when the profiled process may be force-killed before a periodic flush occurs, or when statsFlushIntervalSeconds is set to 0 (manual-only mode).
| Option | Type | Description |
|---|---|---|
| --pid | int | Required. Process ID of the running profiled process |
metreja flush --pid 12345The profiler creates a named Windows event MetrejaFlush_{pid} for IPC signaling. The flush thread responds to both periodic timeouts and manual flush signals.
clear
Delete profiling sessions.
metreja clear -s a1b2c3 # delete one session
metreja clear --all # delete all sessionsSession Config Format
Stored at .metreja/sessions/{sessionId}.json:
{
"sessionId": "a1b2c3",
"metadata": { "scenario": "baseline" },
"instrumentation": {
"maxEvents": 0,
"computeDeltas": true,
"statsFlushIntervalSeconds": 30,
"includes": [
{ "assembly": "MyApp", "namespace": "*", "class": "*", "method": "*" }
],
"excludes": []
},
"output": {
"path": ".metreja/output/{sessionId}_{pid}.ndjson"
}
}| Field | Type | Default | Description |
|---|---|---|---|
| maxEvents | int | 0 | Event cap per session (0 = unlimited) |
| computeDeltas | bool | true | Include delta timing on leave events |
| statsFlushIntervalSeconds | int | 30 | Periodic stats flush interval in seconds (0 = disabled / manual-only mode) |
Output path tokens:
| Token | Replaced With |
|---|---|
| {sessionId} | Session ID from config |
| {pid} | Process ID of the profiled app |
Architecture
Metreja is a two-component system:
Native C++ DLL implementing ICorProfilerCallback3 with ELT3 hooks. Attaches to the .NET runtime via COR_PROFILER environment variables, hooks method enter/leave, and writes NDJSON traces.
C# CLI for session management and trace analysis. Creates session configs, generates profiler environment scripts, provides analysis commands (hotspots, calltree, callers, memory, diff), and the flush command for on-demand stats flushing on running processes.
Data flow: CLI creates session config → generate-env sets profiler env vars → profiled app loads DLL → DLL writes NDJSON → CLI analysis commands read NDJSON.
Building from Source
Prerequisites: Windows 10/11 (x64), .NET 10 SDK (multi-targets .NET 8/9/10), Visual Studio 2022 Build Tools with "Desktop development with C++" workload.
# Build components
dotnet build src/Metreja.Tool/Metreja.Tool.csproj -c Release
msbuild src/Metreja.Profiler/Metreja.Profiler.vcxproj /p:Configuration=Release /p:Platform=x64
# Run integration tests
dotnet test test/Metreja.IntegrationTests/Metreja.IntegrationTests.csproj -c Release
# Format C++ code
scripts/format-cpp.batBuild outputs:
| Output | Path |
|---|---|
| CLI | src/Metreja.Tool/bin/Release/net10.0/metreja.exe |
| Profiler DLL | bin/Release/Metreja.Profiler.dll |
Claude Code Plugin
The metreja-profiler plugin for Claude Code enables fully automated profiling sessions. Install it and ask questions in plain English — Claude handles session setup, profiling, analysis, and fix suggestions automatically.
/plugin marketplace add kodroi/metreja-profiler-marketplace
/plugin install metreja-profiler@metreja-profiler-marketplaceAfter installation, just ask: "This endpoint takes 3 seconds, find out why" or "Where am I wasting memory?"
Requires Metreja.Tool to be installed globally (dotnet tool install -g Metreja.Tool).