Skip to content

Profiling a Plugin Run

Peter McDonald edited this page Aug 16, 2026 · 5 revisions

Profiling a Plug-in Run

Capture a real execution of your plug-in from Dataverse, then replay it on your machine against the exact context the platform passed in, stopping on a breakpoint inside your plug-in while it runs.

In the screenshots, the control to use is outlined in orange, and the environment name and URL are blanked out — you will see your own. The example is a plug-in that onboards a new territory: it validates the name, derives a sales region and a territory code, and traces the result.

Preview feature. Plug-in debugging ships behind the dataverse-powertools.previewFeatures setting, off by default. Tick Preview features in the panel footer and the Debugging block appears.

Before you start

You need Why
A connected environment The profiler runs in the org, not on your machine
A deployed plug-in with a registered step You can only profile a step that exists and is enabled
A unit test project The replay runs as a test. Set up Tests on the plug-in card creates one
.NET Framework, to run the replay test Capturing works on any OS (1.0.7+). Your test project targets .NET Framework, so executing the generated test needs Windows, or mono on macOS/Linux
The Plugin Profiler solution The extension offers to install it for you the first time
The C# extension (ms-dotnettools.csharp) Only for breakpoints — it provides the .NET debugger

Where these actions live

Everything is in the Debugging block of the plug-in card. Active profiles underneath shows anything currently being profiled — worth watching, because a step stays disabled while it is profiled.

The Debugging block on the plug-in card: Profile next run, Download a run, Replay & debug, Generate Replay Test, and the Active profiles list

Two ways to choose which step to profile

An assembly can register many steps, so the first decision is always which step. The two routes differ only in how you pick.

Route A — the panel button

Profile next run starts a capture for this component. If more than one step is profilable it asks "Profile which plugin step?"; with exactly one it goes straight on.

The Profile next run button in the Debugging block, outlined in orange

Route B — the per-step CodeLens

Above every [CrmPluginRegistration(...)] attribute sits a Profile: Off / Profile: On CodeLens. Clicking it profiles that step — the registration you are looking at — so there is no picker to answer. This is the quickest route when you are already in the code, and the label tells you at a glance which steps are armed.

The Profile: Off CodeLens above a CrmPluginRegistration attribute, outlined in orange

Once profiling starts, the label flips to Profile: On. Click it again to stop.

The same CodeLens now reading Profile: On

Step by step

1. Arm the capture

Use either route above. The extension enables profiling on the step so executions route through the profiler, and the output reports what it armed.

2. Trigger your plug-in, then press Continue

The extension waits while you make the thing happen that fires your step — create the record, run the flow, press the button in the app. Then press Continue.

The dialog asking you to trigger the plug-in, with Continue outlined in orange

An asynchronous step runs in a background job, so give it a few seconds after the trigger.

3. Pick the captured run

Continue lists the captured executions. Tick the one you want — it is a multi-select, so you can take several at once.

The captured-run picker with a profile selected

The chosen profile is saved to profiles/ in your component folder.

4. Two buttons, two jobs

Button What it does When you want it
Replay & debug Runs the captured execution back through your plug-in under the debugger, so your breakpoints are hit Now — you are trying to work out what happened
Generate Replay Test Writes the replay test and leaves it, without running it You want to commit it as a regression test and run it in CI

Replay & debug does the generating for you if there is no replay test yet, so you can go straight to it after a capture. Either way you end up with a Replay_<Type>_<timestamp>.cs in your test project, plus a small shared harness (DvptProfileReplay.cs).

The Generate Replay Test button, outlined in orange

With more than one profile in profiles/, it asks "Replay which profile?" first.

5. The generated test

The generated Replay_<Type>_<timestamp>.cs open in the editor

It decodes the .profile, rebuilds the IPluginExecutionContext the platform passed in, and calls your plug-in in-process — no child AppDomain and no live org — so it runs anywhere dotnet test runs, including CI. Run it like any other test: the Testing side bar (including Run Test at Cursor and the gutter icons), Run Tests on the plug-in card, or dotnet test. Green means the captured production context replayed through your code without throwing.

6. Set a breakpoint where you want to look

Put it anywhere in your plug-in — the interesting spot is usually where you read the input or derive something from it.

A breakpoint set on the line that derives the territory code

7. Replay & debug

The Replay & debug button, outlined in orange

The replay starts under the debugger and stops on your breakpoint, inside your plug-in, part-way through the captured execution.

The debugger paused on the breakpoint inside the plug-in

This is the payoff. The call stack runs from the test host into your plug-in's own methods, and because the execution is real, Locals holds the actual Target entity the platform passed in along with whatever your code has derived from it so far — expand Locals in the Variables pane to inspect it. Step, watch and edit-and-re-run as usual.

The debugger paused inside the plug-in, with the call stack running from the test host into the plug-in's own methods

Continue (F5) runs on to the end of the replay and Stop ends the session early; either way the output reports when the replay process finished.

8. Stop profiling

A step left profiled stays disabled, so it will not fire at all until you stop. Either click the Profile: On CodeLens again, or use the stop button in the card's Active profiles block.

What happens under the hood

  1. Enable profiling — the Plugin Profiler clones your step and disables the original, so executions route through the profiler.
  2. Capture — your trigger fires the profiled step; the profiler persists the execution context.
  3. Download — the extension fetches the persisted profile into profiles/.
  4. Generate — it writes a test that decodes the profile (base64 → deflate → report XML → PluginExecutionContext) and invokes your plug-in against it.
  5. Replay under the debugger — the test runs with the .NET debugger attached to the process the test actually executes in, which is what lets a breakpoint inside your plug-in be hit.
  6. Stop profiling — the clone is removed and your original step is re-enabled.

Troubleshooting

"N step(s) are registered for … but all are DISABLED" — profiling was left on. Stop it from the Active profiles list and the step comes back.

"No steps registered for assembly …" — the deployed assembly name does not match this project, or the step never registered. Check the [CrmPluginRegistration] attribute and redeploy with Build & deploy package.

Nothing captured — an asynchronous step may not have finished, or the trigger did not match the step's message/table filter. The profiler only captures what actually fires.

The replay ran but never stopped on the breakpoint — install the C# extension (ms-dotnettools.csharp); without a .NET debugger there is no debug session to attach. The output says so too.

"No unit test project found" — the replay runs as a test. Use Set up Tests on the plug-in card first.

See also

Clone this wiki locally