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.

CommandWhat it does
metreja hotspotsRank methods by self time, inclusive time, call count, or allocations
metreja calltreeExpand a slow method into its full call tree with timing at every level
metreja callersFind who calls a method and how much time each caller contributes
metreja memoryGC counts by generation, pause times, per-type allocation hotspots
metreja analyze-diffCompare two traces to verify a fix actually improved performance
metreja summaryTrace overview: event counts, wall-clock duration, threads, methods
metreja exceptionsRank exception types by frequency with throw-site methods
metreja timelineChronological event listing with tid, event type, and method filtering
metreja threadsPer-thread breakdown: call counts, root time, activity windows
metreja trendMethod performance trend across periodic stats flush intervals
metreja checkCI regression gate: compare two traces, exit non-zero on regression
metreja runLaunch an executable with profiler attached (--detach for GUI apps)
metreja flushTrigger manual stats flush on a running profiled process
metreja listList existing profiling sessions
metreja mergeCombine multiple trace files into one sorted by timestamp
metreja exportConvert traces to speedscope or CSV format for visualization/analysis

Installation

Prerequisites: .NET 8 SDK or later. Windows 10/11 (x64) or macOS 14+ (Apple Silicon).

dotnet tool install -g Metreja.Tool

After 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.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--topint20Number of methods to show
--min-msdouble0.0Minimum time threshold (ms)
--sortstringselfself, inclusive, calls, or allocs
--filterstring[]Filter by method/class/namespace pattern
metreja hotspots trace.ndjson --top 10 --sort self
metreja hotspots trace.ndjson --filter "MyApp.Services" --min-ms 1

calltree

Call tree for a specific method invocation, slowest first.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--methodstringRequired. Method name or pattern
--tidlongFilter by thread ID
--occurrenceint1Which invocation (1 = slowest)
metreja calltree trace.ndjson --method "ProcessOrder"
metreja calltree trace.ndjson --method "ProcessOrder" --occurrence 2

callers

Who calls a method, with call count and timing per caller.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--methodstringRequired. Method name or pattern
--topint20Number of callers to show
metreja callers trace.ndjson --method "SaveChanges" --top 10

memory

GC summary (generation counts, pause times) and per-type allocation hotspots.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--topint20Number of allocation types
--filterstring[]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).

ArgumentTypeDescription
basestringRequired. Base NDJSON file
comparestringRequired. Comparison NDJSON file
metreja analyze-diff baseline.ndjson optimized.ndjson

summary

High-level trace overview: total event count, wall-clock duration, thread count, and unique method count.

metreja summary trace.ndjson

exceptions

Rank exception types by throw frequency, with the top throw-site methods for each type.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--topint20Number of exception types to show
metreja exceptions trace.ndjson --top 10

timeline

Chronological listing of profiler events with thread ID, event type, and optional method/type filtering.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--tidlongFilter by thread ID
--typestringFilter by event type
--methodstringFilter by method name pattern
--topint100Maximum events to show
metreja timeline trace.ndjson --tid 1 --top 50

threads

Per-thread breakdown: call counts, root time, and activity windows.

metreja threads trace.ndjson

trend

Method performance trend across periodic stats flush intervals. Shows how a method's timing evolves over the profiling run.

OptionTypeDefaultDescription
filestringRequired. NDJSON trace file
--methodstringRequired. Method name or pattern
metreja trend trace.ndjson --method "ValidateInventory"

check

CI regression gate. Compares two traces and exits non-zero when any method regresses beyond the threshold. Designed to run in CI pipelines.

Argument / OptionTypeDescription
basestringRequired. Baseline NDJSON file
comparestringRequired. Comparison NDJSON file
--thresholddoubleRegression threshold as percentage (default 10)
metreja check baseline.ndjson pr-build.ndjson --threshold 10
# Exit 0 = no regressions, Exit 1 = regression detected

run

Launch an executable with the profiler automatically attached. Useful for profiling GUI applications or long-running services.

metreja run -s a1b2c3 --detach -- "C:\Program Files\MyApp\app.exe" --config settings.json

flush

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 manual-only flush mode is enabled.

OptionTypeDescription
--pidintRequired. Process ID of the running profiled process
metreja flush --pid 12345

The profiler creates a named Windows event MetrejaFlush_{pid} for IPC signaling. The flush thread responds to both periodic timeouts and manual flush signals.

list

List all existing profiling sessions with their IDs and scenario names.

metreja list

merge

Combine multiple NDJSON trace files into a single file sorted by timestamp.

metreja merge trace1.ndjson trace2.ndjson --output merged.ndjson

export

Convert a trace to speedscope format for visual flame graph exploration, or to CSV for spreadsheet analysis.

metreja export trace.ndjson --output profile.speedscope.json
metreja export trace.ndjson --format csv --output analysis.csv

report

Report an issue to the GitHub repository. Requires the GitHub CLI (gh) installed and authenticated.

OptionTypeDescription
-t, --titlestringRequired. Issue title
-d, --descriptionstringRequired. 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.

OptionTypeDefaultDescription
--scenariostringOptional scenario name
metreja init
metreja init --scenario "before-refactor"

add include / add exclude

Add filter rules controlling which methods get traced. Supports wildcards (*).

OptionTypeDefaultDescription
-s, --sessionstringRequired. Session ID
--assemblystring[]*Assembly name pattern
--namespacestring[]*Namespace pattern
--classstring[]*Class name pattern
--methodstring[]*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.

OptionTypeDefaultDescription
-s, --sessionstringRequired. Session ID
--formatstringbatchbatch, powershell, or shell
--forceboolfalseGenerate 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/sh

set

Configure session settings via subcommands.

SubcommandArgumentsDescription
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 50000Cap event count (0 = unlimited)
set compute-deltas-s ID trueEnable delta timing for performance analysis

validate

Check session configuration for errors. Exits with code 1 on failure.

metreja validate -s a1b2c3

flush

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).

OptionTypeDescription
--pidintRequired. Process ID of the running profiled process
metreja flush --pid 12345

The 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 sessions

Session 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"
  }
}
FieldTypeDefaultDescription
maxEventsint0Event cap per session (0 = unlimited)
computeDeltasbooltrueInclude delta timing on leave events
statsFlushIntervalSecondsint30Periodic stats flush interval in seconds (0 = disabled / manual-only mode)

Output path tokens:

TokenReplaced With
{sessionId}Session ID from config
{pid}Process ID of the profiled app

Architecture

Metreja is a two-component system:

Metreja.Profiler

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.

Metreja.Tool

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

Windows 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.

Windows:

# Build CLI
dotnet build src/Metreja.Tool/Metreja.Tool.csproj -c Release

# Build profiler DLL
msbuild src/Metreja.Profiler/Metreja.Profiler.vcxproj /p:Configuration=Release /p:Platform=x64 "/p:SolutionDir=%CD%\\"

# Run integration tests
dotnet test test/Metreja.IntegrationTests/Metreja.IntegrationTests.csproj -c Release

# Format C++ code
scripts/format-cpp.bat

macOS (Apple Silicon): Requires Xcode Command Line Tools, CMake (brew install cmake), and .NET 10 SDK.

# Build CLI
dotnet build src/Metreja.Tool/Metreja.Tool.csproj -c Release

# Build profiler dylib
scripts/build-macos.sh

# Run integration tests
dotnet test test/Metreja.IntegrationTests/Metreja.IntegrationTests.csproj -c Release

# Format C++ code
scripts/format-cpp.sh

Build outputs:

OutputPath
CLI (Windows)src/Metreja.Tool/bin/Release/net10.0/metreja.exe
CLI (macOS)src/Metreja.Tool/bin/Release/net10.0/metreja
Profiler DLL (Windows)bin/Release/Metreja.Profiler.dll
Profiler dylib (macOS)bin/Release/libMetreja.Profiler.dylib

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-marketplace

After 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).

We use analytics to understand how you use our site and improve your experience. By clicking "Accept", you consent to our use of cookies for analytics.