skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
richlander/dotnet-inspect393 installs

dotnet-inspect-performance

Whole-assembly call-graph leverage ranking and performance triage for libraries (experimental).

How do I install this agent skill?

npx skills add https://github.com/richlander/dotnet-inspect --skill dotnet-inspect-performance
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubpass

    The skill provides instructions for performance analysis of .NET libraries using a CLI tool. It is generally safe but possesses an attack surface for indirect prompt injection through the analysis of external assemblies and data files.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

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.

dnx dotnet-inspect -y -- <command>

Rank by leverage first

Top Leverage ranks members by call-graph leverage: direct callers, Root Reach (distinct entry points that transitively reach a member), fanout, depth, and loop calls. Start here on a whole library, then narrow to a type.

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 Stable selector, Visibility, and Selector. Add --all to include non-public members.

For a whole-library structural report rather than ranked candidates, select the explicit Library Metrics section:

dnx dotnet-inspect -y -- library MyLib.dll -S "Library Metrics"

It reports the Research-owned compiled-IL population metrics without assigning an overall score or source-level meaning.

Triage against rewrite shapes

Library triage is split into kind-scoped sections under @Performance (Performance: Boxing, Performance: Arrays, Performance: Strings, Performance: Closures and Delegates, and more). Structural discovery lists the authored kinds without running analysis; add --effective to retain only kinds with findings for this library. A count executes the selected group and includes zero-row kinds. Type/member scope keeps the focused Performance Triage lens.

dnx dotnet-inspect -y -- library MyLib.dll -D @Performance
dnx dotnet-inspect -y -- library MyLib.dll -D @Performance --effective
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: Strings" --jsonl
dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
  --where "Priority>=high" --top 20 --tsv
dnx dotnet-inspect -y -- library MyLib.dll \
  --triage-shape scan-method-in-loop-call,scan-method-in-recursive-traversal,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 --where "Priority>=high" for the signal-dense first pass, --loop for repeated costs, --min-confidence high|medium|low for an evidence/rewrite confidence floor, --triage-shape for one or more shapes, and --top N for the curated ranked prefix. Supplying any of those flags selects the applicable performance lens automatically. In library row formats, Performance:* flattens two or more populated kind sections into one table with a leading Kind column. If filtering leaves one populated kind, row formats use that kind's concrete schema without Kind; use structured --json when the kind discriminator must remain explicit. @Performance also includes heterogeneous sections, so use it for discovery, counts, Markdown, or JSON documents instead. --top narrows ranked data before rendering; --rows N caps rendered rows afterward. Common shapes include capturing-delegate, box-value-type, generic-parameter-object-box (an unconstrained generic value boxed for object.Equals; it allocates only for value-type instantiations and starts at medium priority unless loop evidence proves repetition), small-array, cache-lookup-factory-delegate (a per-call instance factory passed to ConcurrentDictionary.GetOrAdd), linq-scan-in-loop, scan-method-in-loop-call (a linear-scan helper invoked from a caller loop), scan-method-in-recursive-traversal (a scan repeated once per recursive traversal node), materialize-in-loop (a loop-invariant ToArray/ToList that can be hoisted), string-build-in-loop, string-materialization, enumerator-allocation, async-state-machine, sync-call-in-async (an async method calling a synchronous API with a signature-compatible Async sibling), and allocation-hotspot. Query the algorithmic shapes explicitly: scan helpers stay low-confidence because static analysis cannot prove that the scanned sequence grows with the loop or traversal, so a --min-confidence high pass intentionally excludes them.

string-materialization rows identify exact framework operations that can produce strings: concat, join, format, String.Create, string constructors, interpolated-string handler finalization, StringBuilder.ToString, and Encoding.GetString. Operation names the strategy, for example string.concat or string.builder-finalization. These are potential materializations, not guaranteed allocations: they do not establish new-object allocation, bytes, frequency, or rewrite safety. Confirm a candidate with a representative allocation trace or benchmark before changing it. The first version is intraprocedural; a caller that delegates string creation to a helper does not inherit the helper's exact Finding.

After selecting a sync-call-in-async candidate, project --fields AsyncAlternatives on the member's Call Graph to carry its opportunity count into the leverage view:

dnx dotnet-inspect -y -- member MyType ReadAsync:1 --library MyLib.dll \
  -S "Call Graph" --fields "Fanin,Depth,Loop,AsyncAlternatives"

The graph cue is source-member-level context, not a replacement for triage. Use Performance Triage for the exact Finding, physical EvidenceMethod/IL receipt, and proposed async sibling. In classic async methods the physical call is in generated MoveNext, so the graph deliberately does not fabricate a direct source-method edge to that synchronous API.

The default Triage order keeps Priority separate from Confidence. Priority is a static actionability judgment: directly evidenced algorithmic amplification, avoidable cache-lookup factory allocations, and actionable high allocation weight rank high; recursive scan helpers without shared-source identity and other generic repeated costs rank medium; ordinary one-shot candidates rank low. Escape-unknown small-array rows remain medium even at high weight because no safe stack rewrite is proven. Confidence describes certainty in the evidence and proposed rewrite, so a high-priority, low-confidence row is intentionally an early investigation target rather than a claimed runtime win. Flattened Performance:* row output preserves this global order across kinds.

For registry, pipeline, or object-graph construction that does not match a local rewrite shape, opt into the aggregate allocation fanout:

dnx dotnet-inspect -y -- library MyLib.dll \
  --triage-shape allocation-fanout \
  --order-by "OncePaths desc" --top 20 --tsv

Direct Sites is local to the method. Once Paths 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 Opaque Paths 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: Candidate, Finding (analysis.allocation, analysis.call-site, or analysis.string-materialization), Provenance=exact, Assembly, ModuleVersionId, MethodToken, Operation, Token, EvidenceMethod, and IL. MethodToken identifies the source-facing member, while EvidenceMethod is present when the instruction is mapped to a separate MethodDef; for an async source member, it can name the generated MoveNext body whose offset appears in IL. The exact body coordinate is Assembly + (EvidenceMethod when present, otherwise MethodToken) + IL; ModuleVersionId distinguishes physical module builds when static inputs carry it, and Token is the operand of Operation. Use these fields for runtime/static joins or to carry one triage row into the matching pairwise diff or diff --history confirmation workflow without parsing Evidence text:

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

To ask which source-facing methods with matching performance evidence also contain one rendered C# syntax kind, add a Kind predicate and omit -S:

dnx dotnet-inspect -y -- library MyLib.dll \
  --where "Kind=InvocationExpression" \
  --where "Finding=analysis.call-site" \
  --where "Shape=sync-call-in-async" \
  --where "Confidence>=medium" --jsonl

This emits Body Shapes, not Performance rows. The typed performance opportunities narrow source MethodDef bodies before decompilation; run the Performance query separately when its candidate, evidence, and IL receipt are needed. --top and --order-by do not compose with Body Shapes; use --rows to limit rendered syntax matches.

Aggregate rows such as allocation-hotspot use Provenance=aggregate and have a pt~ candidate id but no exact source Finding, operation, or token. A composite repeated-scan judgment can separately retain the exact local call that supports it in SupportingFinding, SupportingOperation, SupportingToken, SupportingEvidenceMethod, and SupportingIL. Those fields are a runtime correspondence coordinate, not the aggregate row's source Finding or candidate identity. Provenance=unmatched 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:

dnx dotnet-inspect -y -- library MyLib.dll -S "Performance:*" \
  --where "CallerLoop=direct" --json

CallerLoopDepth and CallerLoopWitness identify the deterministic invocation site. This evidence does not change the row's local Loop, 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 Resource Triage library section to find ArrayPool<T> acquisitions whose exact def-use path reaches an external-input boundary before modeled cleanup:

dnx dotnet-inspect -y -- library MyLib.dll -S "Resource Triage" --jsonl

Treat pool-churn-on-exception 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 Candidate, Finding=analysis.resource-lifecycle, Acquire IL, Boundary IL, and Boundary 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. async-state-machine 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:

dnx dotnet-inspect -y -- diff --history --package MyLib@1.0.0..2.0.0 \
  -t MyType -m HotPath \
  --finding analysis.allocation --at first --at last

Repeat --at for sparse probes or use --at all 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:

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. PairFinding.Added confirms an allocation occurrence was introduced, while Present, Removed, and Changed 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:

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. PairFinding.Added confirms a new call occurrence, such as a newly introduced Enumerable.ToArray. Changed can show that an existing call moved into a loop or changed dispatch/opcode facets. Use the single-version Calls section while probing versions; use this final adjacent comparison as the onset proof.

Drill a candidate

Call Graph is a bounded bidirectional graph: inbound callers up to entry points and outbound calls, centred on the selected member. Project per-node cost with --fields (alloc, copy, unsafe, reflection, throw/exception, catch/finally). Its default Markdown edge table is best for comparing relationships and cost cues. Use --tree when the path toward or away from the candidate matters, --mermaid for a standalone diagram, or --markdown --mermaid to embed the diagram. Use --tsv or --jsonl 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.

Call Graph has not adopted --envelope; use --jsonl or --tsv for edge rows. Document --json currently emits the surrounding Type result rather than graph edges. Exact-member --share url is a separate public API Overview projection and does not retain the performance or Call Graph lens.

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

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/richlander/dotnet-inspect/dotnet-inspect-performance">View dotnet-inspect-performance on skillZs</a>