dotnet-inspect: performance analysis and triage
Use this skill to find the members worth optimizing or hardening first in a .NET
assembly, and to triage them against actionable rewrite shapes. This analysis is
experimental; section names and signal sets may change between releases.
bash
dnx dotnet-inspect -y -- <command>
Rank by leverage first
ranks members by call-graph leverage: direct callers,
(distinct entry points that transitively reach a member), fanout, depth,
and loop calls. Start here on a whole library, then narrow to a type.
bash
dnx dotnet-inspect -y -- library MyLib.dll -S "Top Leverage"
dnx dotnet-inspect -y -- type MyType --library MyLib.dll --all -S "Top Leverage"
Ranking rows carry a copyable
selector,
, and
.
Add
to include non-public members.
Triage against rewrite shapes
Library triage is split into kind-scoped sections under
(
,
,
Performance: Closures and delegates
, and more). Start with discovery or counts, then select the group or
one concrete kind. Type/member scope keeps the focused
lens.
bash
dnx dotnet-inspect -y -- library MyLib.dll -D @Performance
dnx dotnet-inspect -y -- library MyLib.dll -S @Performance --count
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance: Boxing" --jsonl
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
--loop --min-confidence high --top 20 --tsv
dnx dotnet-inspect -y -- library MyLib.dll \
--triage-shape scan-method-in-loop-call,linq-scan-in-loop,string-build-in-loop \
--top 20 --tsv
dnx dotnet-inspect -y -- library MyLib.dll --triage-shape capturing-delegate --top 10 --jsonl
Target IL-visible costs (allocations: box, newarr, delegate newobj,
ToArray/ToList/Concat), not JIT-handled concerns (isinst/castclass folding,
devirtualization, bounds-check elimination, null-check folding).
Use
for repeated hot costs,
--min-confidence high|medium|low
for a
confidence floor,
for one or more shapes, and
for the
curated ranked prefix. Supplying any of those flags selects the applicable
performance lens automatically. In library row formats,
flattens two or more populated kind sections into one table with a leading
column. If filtering leaves one populated kind, row formats use that
kind's concrete schema without
; use structured
when the kind
discriminator must remain explicit.
also includes heterogeneous
sections, so use it for discovery, counts, Markdown, or JSON documents instead.
narrows ranked data before rendering;
caps rendered rows
afterward. Common shapes
include
,
,
,
,
(a linear-scan helper invoked
from a caller loop),
(a loop-invariant
/
that can be hoisted),
,
,
, and
. Query the algorithmic shapes
explicitly:
stays low-confidence because static
analysis cannot prove that the scanned sequence grows with the caller's loop,
so a
pass intentionally excludes it.
For registry, pipeline, or object-graph construction that does not match a local
rewrite shape, opt into the aggregate allocation fanout:
bash
dnx dotnet-inspect -y -- library MyLib.dll \
--triage-shape allocation-fanout \
--order-by "OncePaths desc" --top 20 --tsv
is local to the method.
composes exact
intra-assembly callsites and counts repeated callsites separately; conditional,
repeated, unknown, cached, and opaque paths remain separate columns. Treat this
as IL-visible normal-return-path quantity, not runtime bytes or observed
frequency. A high
count means virtual, external, delegate,
recursive, or runtime-library work still needs a drill or profiler.
Exact rows retain machine-readable provenance from the native Analysis
producer in structured JSON:
,
(
or
),
,
,
, and
. Use these fields for runtime/static joins or to
carry one triage row into the matching
/
confirmation workflow
without parsing
text:
bash
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
--where "Finding=analysis.allocation" --where "Operation=box" --json
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
--where "Finding=analysis.call-site" --json
Aggregate rows such as
use
and have
a
candidate id but no exact source Finding, operation, or token.
flags an instruction-level row that did not join to the
expected producer census.
Select direct caller-loop repetition
A once-per-call allocation can still be repeated by an upstream caller's loop.
Select rows with an exact direct invocation receipt:
bash
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
--where "CallerLoop=direct" --json
and
identify the deterministic invocation
site. This evidence does not change the row's local
, multiplicity,
confidence, weight, candidate identity, or default rank. Use it to select a
candidate for profiling, not as proof that the caller is hot or that the loop
executes.
Only resolved invocation edges qualify. Function loads and callback
registration do not prove callback execution, and recursive traversal does not
prove realized depth or frequency. Do not infer either case into caller-loop
evidence; require runtime evidence or a stronger product-owned invocation
contract.
Triage exception-path pool churn
Select the explicit
library section to find
acquisitions whose exact def-use path reaches an external-input boundary before
modeled cleanup:
bash
dnx dotnet-inspect -y -- library MyLib.dll -S "Resource Triage" --jsonl
Treat
as a profiling and hardening candidate, not a
permanent-memory-leak or memory-corruption accusation. Static analysis proves
the unprotected boundary shape and API evidence, not runtime frequency. Use
,
Finding=analysis.resource-lifecycle
,
,
,
and
to retain exact provenance while drilling the method. Each
boundary is one row; a multi-boundary candidate repeats its candidate and
acquisition fields so every operation stays paired with its own IL offset.
Not every shape is a pure hot-path win.
is reported as
amortized (low confidence) unless the allocation sits in a loop: async lowering
moves work into a state object rather than eliminating it, often once per
call/enumeration/subscription. Treat amortized rows as context, and confirm a
real per-item cost with a profiler before optimizing.
Confirm when an allocation appeared
Correlate one method's native allocation census across caller-selected package
cells:
bash
dnx dotnet-inspect -y -- timeline --package MyLib@1.0.0..2.0.0 \
-t MyType -m HotPath \
--finding analysis.allocation --at first --at last
Repeat
for sparse probes or use
for an explicitly bounded
dense traversal. These probes locate a candidate old/new boundary; they do not
establish onset. Confirm one method's adjacent pair with Analysis's native
allocation Findings:
bash
dnx dotnet-inspect -y -- diff --package MyLib@1.4.0..1.5.0 \
-t MyType -m HotPath \
--finding analysis.allocation
The method target must resolve at one or both endpoints.
confirms an allocation occurrence was introduced, while
,
,
and
identify a wrong boundary, disappearance, or changed allocation
facets. The command does not traverse versions; the caller owns the search
policy and bound.
Trace a likely cause to a new call
After confirming an allocation boundary, compare the same caller method's
direct-call census:
bash
dnx dotnet-inspect -y -- diff --package MyLib@1.4.0..1.5.0 \
-t MyType -m HotPath \
--finding analysis.call-site
The target method is the caller and each row identifies a callee.
confirms a new call occurrence, such as a newly introduced
.
can show that an existing call moved into a loop
or changed dispatch/opcode facets. Use the single-version
section while
probing versions; use this final adjacent comparison as the onset proof.
Drill a candidate
is a bounded bidirectional graph: inbound callers up to entry
points and outbound calls, centred on the selected member. Project per-node cost
with
(alloc, copy, unsafe, reflection, throw/exception,
catch/finally). Its default Markdown edge table is best for comparing
relationships and cost cues. Use
when the path toward or away from the
candidate matters,
for a standalone diagram, or
to embed the diagram. Use
or
when a
script will consume the same edge rows. Requested cost cues remain annotations
in the node labels; they do not become separate machine columns.
bash
dnx dotnet-inspect -y -- member MyType Method:1 --library MyLib.dll -S "Call Graph,Facts"
dnx dotnet-inspect -y -- member MyType Method:1 --library MyLib.dll -S "Call Graph" --fields "Throw,Catch,Finally"
dnx dotnet-inspect -y -- member MyType Method:1 --library MyLib.dll -S "Call Graph" --fields "Alloc,Loop" --tree
dnx dotnet-inspect -y -- member MyType Method:1 --library MyLib.dll -S "Call Graph" --jsonl