Skip to content

[browser][coreCLR] event pipe and browser profiler#126324

Open
pavelsavara wants to merge 2 commits into
dotnet:mainfrom
pavelsavara:browser_EP_coreclr
Open

[browser][coreCLR] event pipe and browser profiler#126324
pavelsavara wants to merge 2 commits into
dotnet:mainfrom
pavelsavara:browser_EP_coreclr

Conversation

@pavelsavara
Copy link
Copy Markdown
Member

@pavelsavara pavelsavara commented Mar 30, 2026

Summary

This change builds on top of EventPipe-for-CoreCLR (#128745) and adds two WASM profiling experiences for the CoreCLR interpreter on single-threaded browser/WASI builds:

  1. EventPipe CPU sampling profiler — a cooperative, single-threaded sampling profiler that produces .nettrace CPU samples consumable by dotnet-trace / the JS collectCpuSamples API.
  2. Browser DevTools profiler — emits performance.measure() calls so managed method execution shows up as a nested flame chart in the browser Performance tab.

Both are opt-in via the DOTNET_WasmPerformanceInstrumentation MethodSet filter and are wired up through the existing WasmPerformanceInstrumentation MSBuild property (Mono-compatible syntax).

Motivation

On the multi-threaded runtime the EventPipe sample profiler runs on a dedicated thread that suspends the target. On single-threaded WASM there is no such thread, so the previous ep_rt_sample_profiler_* hooks were no-ops and CPU sampling was unavailable for CoreCLR. This adds a cooperative sampling mechanism driven by the interpreter, matching the capability Mono already provides on the browser.

How it works

New interpreter opcodes

Three opcodes are added in intops.def:

  • INTOP_PROF_SAMPLEPOINT — emitted at method entry and at backward branches (loop back-edges, alongside the existing INTOP_SAFEPOINT). Drives the EventPipe sampling profiler.
  • INTOP_PROF_ENTER / INTOP_PROF_LEAVE — emitted at method entry/return (and tail-call / exception unwind paths). Drive the browser DevTools profiler.

The interpreter compiler only emits these when the method matches the WasmPerformanceInstrumentation MethodSet filter, and only on PERFTRACING_DISABLE_THREADS (single-threaded) builds. INTOP_PROF_ENTER/INTOP_PROF_LEAVE are additionally gated on TARGET_BROWSER.

EventPipe CPU sampling (single-threaded)

ep-rt-coreclr-wasm-sampling.cpp implements the ep_rt_coreclr_sample_profiler_* callbacks (now wired from ep-rt-coreclr.h instead of being no-ops). SamplingProfiler_OnSamplepoint() is invoked from the interpreter at samplepoints and:

  • Uses a fast skip-counter path to avoid overhead on every samplepoint.
  • Adaptively recomputes the skip count using an exponential-moving-average against the desired sampling interval (the same approach as Mono's ep-rt-mono-runtime-provider.c).
  • Walks the managed stack and writes a managed sample profile event when due.

On multi-threaded builds the callbacks remain no-ops (the threaded profiler is used).

Browser DevTools profiler

browserprofiler.cpp maintains a shadow stack of method enter/leave timings and calls ds_rt_browser_performance_measure() (→ performance.measure()) so methods appear as nested entries in the browser Performance/flame chart. Recording is rate-limited with an adaptive skip counter; the shadow stack always tracks enter/leave for correctness, and a parent frame is marked for recording when a child is recorded so the chart nests properly. Leave only pops when the top frame matches, guarding against mismatched enter/leave for filtered-out methods.

Configuration / MSBuild

  • New RELEASE_CONFIG_METHODSET(WasmPerformanceInstrumentation) in interpconfigvalues.h. jitStartup enables the profilers when the filter is non-empty, before any managed code is compiled.
  • BrowserWasmApp.CoreCLR.targets and Microsoft.NET.Sdk.WebAssembly.Browser.targets translate the Mono-style WasmPerformanceInstrumentation property (e.g. all,interval=0) into CoreCLR env vars (DOTNET_WasmPerformanceInstrumentation, sampling-rate var), with all mapped to the MethodSet wildcard *. Native build is forced on when the property is set.

Tests

  • Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests is added to BuildWasmAppsJobsListCoreCLR.txt so it runs for CoreCLR.
  • EventPipeDiagnosticsTests.cs: removed the class-level mono category so it runs on CoreCLR; split the AOT (Mono-only) case into its own native-mono [Fact] while the Debug/Release non-AOT cases run for both runtimes.
  • browser-eventpipe sample updated to demonstrate collectCpuSamples / collectGcDump / collectMetrics from the DevTools console.
image image image image

@pavelsavara pavelsavara added this to the 11.0.0 milestone Mar 30, 2026
@pavelsavara pavelsavara self-assigned this Mar 30, 2026
Copilot AI review requested due to automatic review settings March 30, 2026 16:51
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture area-System.Diagnostics os-browser Browser variant of arch-wasm labels Mar 30, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @dotnet/area-system-diagnostics
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables EventPipe/diagnostic-server plumbing for CoreCLR in browser by turning on FEATURE_PERFTRACING while keeping FEATURE_EVENT_TRACE disabled, and adds the browser-side scheduling + WebSocket/JS transport needed to drive the diagnostic server loop.

Changes:

  • Enable FEATURE_PERFTRACING for browser CoreCLR and gate ETW/EventTrace-specific code behind FEATURE_EVENT_TRACE (including EventPipe codegen changes).
  • Add browser-native diagnostic server job queue + scheduler callback wiring (SystemJS_*DiagnosticServer*).
  • Add JS diagnostics client/transport (WebSocket + in-browser “js://” scenarios) and expose ds_rt_websocket_* + dotnetApi.collect* helpers.

Reviewed changes

Copilot reviewed 42 out of 43 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
src/native/libs/System.Native.Browser/utils/scheduling.ts Runs the diagnostic server callback alongside other background callbacks; abort clears pending diagnostic server timer id.
src/native/libs/System.Native.Browser/native/scheduling.ts Adds SystemJS_ScheduleDiagnosticServer scheduling hook using safeSetTimeout.
src/native/libs/System.Native.Browser/native/index.ts Exposes diagnostic server scheduler + callback through the native exports table; exports ds websocket shims.
src/native/libs/System.Native.Browser/native/diagnostics.ts Adds native-side re-exports for ds_rt_websocket_* via diagnostics cross-module exports.
src/native/libs/System.Native.Browser/diagnostics/types.ts Introduces diagnostics protocol/session/provider types and enums used by the JS diagnostics client.
src/native/libs/System.Native.Browser/diagnostics/index.ts Wires diagnostics exports table and installs JS diagnostics client + dotnetApi helpers.
src/native/libs/System.Native.Browser/diagnostics/dotnet-gcdump.ts Adds JS helper to collect gcdump trace via diagnostic server protocol.
src/native/libs/System.Native.Browser/diagnostics/dotnet-cpu-profiler.ts Adds JS helper to collect CPU samples via diagnostic server protocol.
src/native/libs/System.Native.Browser/diagnostics/dotnet-counters.ts Adds JS helper to collect metrics via diagnostic server protocol.
src/native/libs/System.Native.Browser/diagnostics/diagnostics.ts Implements browser-side ds_rt_websocket_* transport and DS router reconnection support.
src/native/libs/System.Native.Browser/diagnostics/diagnostics-ws.ts Implements WebSocket-based diagnostic connection wrapper.
src/native/libs/System.Native.Browser/diagnostics/diagnostics-js.ts Implements in-browser “JS diagnostic client” session handling and scenario-based commands.
src/native/libs/System.Native.Browser/diagnostics/common.ts Shared base queue/recv logic and trace download helper.
src/native/libs/System.Native.Browser/diagnostics/client-commands.ts Implements serialization for diagnostic IPC commands (advertise, EventPipe commands, etc.).
src/native/libs/System.Native.Browser/ds.h Adds placeholder header for diagnostic server C support.
src/native/libs/System.Native.Browser/ds.c Adds native diagnostic server job queue + callback executor for browser builds.
src/native/libs/System.Native.Browser/CMakeLists.txt Links ds.c into System.Native.Browser-Static.
src/native/libs/Common/JavaScript/types/public-api.ts Updates diagnostics public types (providerName).
src/native/libs/Common/JavaScript/types/exchange.ts Extends exchange tables/types for new native browser + diagnostics exports.
src/native/libs/Common/JavaScript/types/ems-ambient.ts Adds ambient symbol + timer id tracking for diagnostic server callback scheduling.
src/native/libs/Common/JavaScript/loader/dotnet.d.ts Updates generated public type surface (providerName).
src/native/libs/Common/JavaScript/cross-module/index.ts Updates cross-module table mapping for new native browser + diagnostics exports.
src/native/eventpipe/ds-ipc-pal-websocket.h Adds C linkage guards for websocket PAL APIs.
src/mono/mono/utils/mono-threads.h Renames/aligns DS job queue API name to SystemJS_DiagnosticServerQueueJob.
src/mono/mono/utils/mono-threads-wasm.h Renames DS exec callback export to SystemJS_ExecuteDiagnosticServerCallback.
src/mono/mono/utils/mono-threads-wasm.c Renames DS queue/exec functions for browser single-threaded mode.
src/mono/mono/mini/mini-wasm.c Updates exported symbol name for DS exec callback.
src/mono/mono/eventpipe/ep-rt-mono.h Updates DS job rescheduling callsite to new queue function name.
src/mono/browser/runtime/types/internal.ts Updates runtime helper name for DS exec callback.
src/mono/browser/runtime/exports.ts Exposes renamed DS exec callback in runtime exports.
src/mono/browser/runtime/diagnostics/common.ts Calls renamed DS exec callback from mono browser diagnostics event loop.
src/mono/browser/runtime/cwraps.ts Updates cwrap signature to match renamed DS exec callback export.
src/coreclr/vm/qcallentrypoints.cpp Gates NativeRuntimeEventSource QCalls behind FEATURE_EVENT_TRACE while keeping EventPipe QCalls under FEATURE_PERFTRACING.
src/coreclr/vm/nativeeventsource.cpp Adds non-ETW stubs when FEATURE_PERFTRACING is enabled but FEATURE_EVENT_TRACE is disabled.
src/coreclr/vm/gcenv.ee.cpp Wraps ETW-only GC analysis events with FEATURE_EVENT_TRACE guards.
src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h Adds browser single-threaded job queue integration via SystemJS_DiagnosticServerQueueJob; gates ETW-only provider init behind FEATURE_EVENT_TRACE.
src/coreclr/vm/eventing/eventpipe/CMakeLists.txt Adds --noetwcallbacks option for EventPipe codegen when FEATURE_EVENT_TRACE is off.
src/coreclr/vm/eventing/CMakeLists.txt Makes dependency on eventprovider conditional.
src/coreclr/scripts/genEventPipe.py Adds --noetwcallbacks support to omit ETW callback wiring in generated provider code.
src/coreclr/nativeaot/Runtime/disabledruntimeeventinternal.cpp Fixes a typo in a comment.
src/coreclr/clrfeatures.cmake Enables FEATURE_PERFTRACING for browser targets; adjusts feature enablement logic.
src/coreclr/clrdefinitions.cmake Ensures FEATURE_PERFTRACING defines and avoids creating dummy targets when perftracing is enabled.
src/coreclr/clr.featuredefines.props Enables perf tracing for browser build configuration.

Comment thread src/native/libs/System.Native.Browser/diagnostic_server_jobs.c
Comment thread src/native/libs/System.Native.Browser/diagnostics/common.ts
Comment thread src/native/libs/System.Native.Browser/diagnostics/diagnostics-ws.ts Outdated
Comment thread src/native/libs/Common/JavaScript/types/exchange.ts Outdated
Comment thread src/native/libs/System.Native.Browser/diagnostics/dotnet-gcdump.ts Outdated
Comment thread src/native/libs/Common/JavaScript/types/public-api.ts
Comment thread src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h Outdated
Copilot AI review requested due to automatic review settings March 30, 2026 17:05
@pavelsavara pavelsavara changed the title [browser][coreCLR] event pipe [browser][coreCLR] event pipe and browser profiler Apr 22, 2026
@pavelsavara
Copy link
Copy Markdown
Member Author

image image

Log

    Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests.BlazorEventPipeTestWithMetrics [FINISHED] Time: 85.5664929s
    Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests.BlazorEventPipeTestWithHeapDump [FINISHED] Time: 69.5142733s
    Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests.BlazorEventPipeTestWithCpuSamples(config: Debug, aot: False) [FINISHED] Time: 33.8440201s
    Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests.BlazorEventPipeTestWithCpuSamples(config: Release, aot: False) [FINISHED] Time: 63.339615s

@pavelsavara pavelsavara force-pushed the browser_EP_coreclr branch from de598c6 to 31be2f2 Compare June 3, 2026 17:46
if (frame->shouldRecord)
{
SString methodName;
TypeString::AppendMethodInternal(methodName, frame->pMethod, TypeString::FormatNamespace);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would appreciate advice about this for CoreCLR VM. This is possibly expensive call and possibly breaching VM contract, I don't know. Is there better way ?

EventPipeStackContents stackContents;
EventPipeStackContents *pStackContents = ep_stack_contents_init(&stackContents);

if (ep_rt_coreclr_walk_managed_stack_for_thread(s_currentSamplingThread, pStackContents)
Copy link
Copy Markdown
Member Author

@pavelsavara pavelsavara Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this OK with regards to VM contract ?

What kind of CONTRACT macro belongs here ?
It's called directly from interp main switch.

@jkotas @BrzVlad

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-System.Diagnostics os-browser Browser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants