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

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

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 pavelsavara requested a review from thaystg as a code owner February 17, 2025 14:10
Copy link
Contributor

@Copilot 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.

Copilot reviewed 12 out of 27 changed files in this pull request and generated 1 comment.

Files not reviewed (15)
  • src/mono/mono/metadata/jit-icall-reg.h: Language not supported
  • src/mono/mono/mini/interp/interp.c: Language not supported
  • src/mono/mono/mini/interp/jiterpreter.h: Language not supported
  • src/mono/mono/mini/interp/mintops.def: Language not supported
  • src/mono/mono/mini/interp/transform.c: Language not supported
  • src/mono/mono/mini/mini-profiler.c: Language not supported
  • src/mono/mono/mini/mini-runtime.c: Language not supported
  • src/mono/mono/mini/mini.c: Language not supported
  • src/mono/mono/mini/mini.h: Language not supported
  • src/mono/mono/mini/trace.c: Language not supported
  • src/mono/mono/mini/trace.h: Language not supported
  • src/mono/mono/profiler/browser.c: Language not supported
  • src/mono/browser/runtime/exports-binding.ts: Evaluated as low risk
  • src/mono/browser/runtime/es6/dotnet.es6.lib.js: Evaluated as low risk
  • src/mono/browser/runtime/jiterpreter-trace-generator.ts: Evaluated as low risk
Comments suppressed due to low confidence (3)

src/mono/browser/runtime/profiler.ts:96

  • The new function 'mono_wasm_profiler_record' should be covered by tests to ensure it works as expected.
export function mono_wasm_profiler_record (method: MonoMethod, start: number): void {

src/mono/browser/runtime/jiterpreter-support.ts:1619

  • Ensure 'frame' is a valid local variable before using it. Add a check to verify 'frame' is defined.
builder.local("frame");

src/mono/browser/runtime/jiterpreter-support.ts:1617

  • The error message should include the opcode value for better debugging. Consider changing to Unimplemented profiler event: ${opcode}.
throw new Error(`Unimplemented profiler event ${opcode}`);

src/mono/browser/runtime/types/internal.ts Outdated Show resolved Hide resolved
src/mono/mono/mini/mini.c Outdated Show resolved Hide resolved
Copy link
Member

@kg kg left a comment

Choose a reason for hiding this comment

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

LGTM (I'm not an expert on aot or mini jit)

- rename mono_jiterp_free_method_data_js to mono_wasm_free_method_data
- use that in profiler
- switch profiler_stack_frames to fixed size buffer
src/mono/mono/mini/mini.c Show resolved Hide resolved
src/mono/mono/mini/mini.c Show resolved Hide resolved

MonoBasicBlock *prev_cbb = cfg->cbb;
cfg->cbb = bblock;
mini_profiler_emit_samplepoint (cfg);
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem correct. We are inserting code at the end of the basic block. Assuming that it is common for a basic block to end with a branch opcode, this would make the sample point not always hit. For reference, insert_safepoint adds the instruction at the beginning of the basic block.

Copy link
Member Author

Choose a reason for hiding this comment

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

@BrzVlad Could you please suggest/commit a fix, I was struggling with this. Many thanks!

Copy link
Member

@BrzVlad BrzVlad Feb 19, 2025

Choose a reason for hiding this comment

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

A simple solution that I suspect could work would be to create a new tmp_bblock here instead (NEW_BBLOCK) and set it as the cbb. Then you do the emit as before into tmp_bblock and once you are done you just move all instructions together from the temporary basic block to the one that you were planning to emit. This would mean that you just update the successor of last_ins of tmp_bblock to point to bblock->code and vice versa, and then set bblock->code to tmp_bblock->code. Hopefully everything would work just like that.

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.

5 participants