-
Notifications
You must be signed in to change notification settings - Fork 2
Profiling a Plugin 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.previewFeaturessetting, off by default. Tick Preview features in the panel footer and the Debugging block appears.
| 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 |
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.

An assembly can register many steps, so the first decision is always which step. The two routes differ only in how you pick.
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.

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.

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

Use either route above. The extension enables profiling on the step so executions route through the profiler, and the output reports what it armed.
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.

An asynchronous step runs in a background job, so give it a few seconds after the trigger.
Continue lists the captured executions. Tick the one you want — it is a multi-select, so you can take several at once.

The chosen profile is saved to profiles/ in your component folder.
| 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).

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

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.
Put it anywhere in your plug-in — the interesting spot is usually where you read the input or derive something from it.


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

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.

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.
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.
- Enable profiling — the Plugin Profiler clones your step and disables the original, so executions route through the profiler.
- Capture — your trigger fires the profiled step; the profiler persists the execution context.
-
Download — the extension fetches the persisted profile into
profiles/. -
Generate — it writes a test that decodes the profile (base64 → deflate → report XML →
PluginExecutionContext) and invokes your plug-in against it. - 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.
- Stop profiling — the clone is removed and your original step is re-enabled.
"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.
- Debugging Plugins — the overview, trace logs, and the macOS/Linux route
- Plugins — the project type itself