Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser] samplepoint instrumentation into Mono profiler #112352

Merged
merged 30 commits into from
Feb 25, 2025

Conversation

pavelsavara
Copy link
Member

@pavelsavara pavelsavara commented Feb 10, 2025

Motivation

Collect sample stack traces in the single-threaded browser.
Single threaded environment can't have dedicated thread for "stop-the-world + collect stack traces" implementation.
So we need to collect samples by instrumenting the code instead.

Previously we implemented browser based profiler which collects "sample" on every method entry/leave. In realistic application, that generates too much traffic for the browser profiler.

This enhancement could also be used to implement sampling profiler with ST diagnostic server.

Goal

  • add new samplepoint instrumentation into Mono profiler, use the existing enter/leave too.
  • inject the event/call into same locations as GC safepoint.
    • we can skip emitting it at the method start, because there is already method_enter event/instrumentation.
    • This is also same location where cooperative MT would stop-the-world to take a sample.
  • take sample on each Nth visit to the samplepoint (because measuring current time is more expensive)
  • keep updating the size of N to match expected sample time interval

Contributes to #76316

Non-goal: the browser profiler is not compatible with MT.

Changes

Interop

  • new interp IR instruction MINT_PROF_SAMPLEPOINT
  • new profiler event method_samplepoint
  • new interp macro INTERP_PROFILER_RAISE

Jiterp

  • new jiterp helper append_profiler_event calling new mono_jiterp_prof_enter, mono_jiterp_prof_samplepoint, mono_jiterp_prof_leave

AOT

  • new Mono JIT method mini_profiler_emit_samplepoint
  • example of code emitted by AOT with call $mono_profiler_raise_method_samplepoint

browser profiler

  • new config option sampleIntervalMs for browser profiler. 1ms by default. All samples when set to 0.
  • changes JavaScript called only to get time mono_wasm_profiler_now and mono_wasm_profiler_record
  • all sampling logic in browser.c new should_record_frame
  • stack of start times profiler_stack_frames and skip counter sample_skip_counter
  • default sample interval 10ms
  • callspec parametr to profiler allows to filter the instrumentation, for example by namespace callspec=N:Sample.FooNs

How to use

Interpreter

  • dotnet workload install wasm-tools

In the project file

<WasmProfilers>browser;</WasmProfilers>

In your main.js

import { dotnet } from './dotnet.js'
dotnet.withConfig({
        browserProfilerOptions: {
            sampleIntervalMs: 10, // optional
        }
    })
    .create();

or in your Blazor app

Blazor.start({
            configureRuntime: function (builder) {
                builder.withConfig({
                        browserProfilerOptions: {
                            sampleIntervalMs: 10, // optional
                        }
                });
            }
        });

AOT

You can add following, so that the native code produced by AOT has symbols, which are visible in the browser devtools profiler.

<RunAOTCompilation>true</RunAOTCompilation>
<RunAOTCompilationAfterBuild>true</RunAOTCompilationAfterBuild>
<WasmNativeStrip>false</WasmNativeStrip>
<WasmProfilers>browser</WasmProfilers>
<WasmNativeDebugSymbols>true</WasmNativeDebugSymbols>
<WasmBuildNative>true</WasmBuildNative>

callspec

If you want to filter profiled methods, you can use callspec

Callspec is documented here https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md#trace-monovm-profiler-events-during-startup

browserProfilerOptions: {
    callSpec: "N:SampleNamespace" // optional
}

Your for AOT needs to match callspec in the browserProfilerOptions in JS

<WasmProfilers>browser:callspec=N:Sample;</WasmProfilers>

@pavelsavara pavelsavara added arch-wasm WebAssembly architecture area-Diagnostics-mono os-browser Browser variant of arch-wasm labels Feb 10, 2025
@pavelsavara pavelsavara added this to the 10.0.0 milestone Feb 10, 2025
@pavelsavara pavelsavara self-assigned this Feb 10, 2025
@pavelsavara pavelsavara changed the title [browser] add sample point instrumentation into Mono profiler [browser] Sample point instrumentation into Mono profiler Feb 10, 2025
@pavelsavara
Copy link
Member Author

pavelsavara commented Feb 10, 2025

Interpreter: 1ms, skip rate between 200-500 sample points.
image

@pavelsavara

This comment was marked as resolved.

@pavelsavara

This comment was marked as resolved.

@hakenr
Copy link
Member

hakenr commented Feb 11, 2025

Hi @pavelsavara,
I'm in! I'll try to reach you via Teams.

@pavelsavara
Copy link
Member Author

My testing shows that enter/leave events are unbalanced.
I think my adding samplepoint in instrumentation didn't change that.
But the design of measuring the difference between method entry and method end requires that the events are balanced.

I will test it further to see if this could be fixed in the instrumentation or if this must be detected and survived by the receiving side.

@kg
Copy link
Member

kg commented Feb 21, 2025

I think you can't randomly record only a % of enter/leave events, or if you do you need to ensure that they are balanced - for example if you record an entry for method x the matching return also needs to be recorded. So you can only pick a % of enters to record but you need to do the check for every LEAVE as to whether this current stack frame previously recorded an enter.

…CONTEXT

- fix flag detection for both variants
- insert enter_profiling only when not inlining, to match leave event behavior
…e_exception_internal - also when MONO_EXCEPTION_CLAUSE_NONE

- more asserts in browser profiler
@pavelsavara pavelsavara requested a review from BrzVlad February 24, 2025 22:35
@BrzVlad
Copy link
Member

BrzVlad commented Feb 25, 2025

LGTM

@pavelsavara
Copy link
Member Author

/ba-g CI timeouts and known issues

@pavelsavara pavelsavara merged commit 0473604 into dotnet:main Feb 25, 2025
152 of 171 checks passed
@pavelsavara pavelsavara deleted the mono_profile_safepoint branch February 25, 2025 13:47
Copy link
Member

@lateralusX lateralusX left a comment

Choose a reason for hiding this comment

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

Looks like I was late to the party (was OOF last week), but add my comments/questions here anyways, either for future follow ups or PR's.

#define INTERP_PROFILER_RAISE(name_lower, name_upper) \
if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_ ## name_lower) && \
(frame->imethod->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper ## _CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper)))) { \
MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);\
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to heap allocate this? Looks like stack allocation should be enough, unless we are afraid consuming stack space since the MonoProfilerCallContext that includes MonoContext.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the place that possibly deals with MonoProfilerCallContext -> context

emit_fill_call_ctx (MonoCompile *cfg, MonoInst *method, MonoInst *ret)

What would happen if we removed context field completely ?

@@ -534,6 +534,22 @@ EMSCRIPTEN_KEEPALIVE const char * mono_wasm_method_get_name (MonoMethod *method)
return res;
}

EMSCRIPTEN_KEEPALIVE const char * mono_wasm_method_get_name_ex (MonoMethod *method) {
Copy link
Member

Choose a reason for hiding this comment

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

If you hand out ownership of this pointer and expect others to free it, it shouldn't be const.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-wasm WebAssembly architecture area-Diagnostics-mono os-browser Browser variant of arch-wasm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants