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

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/mono/browser/runtime/cwraps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ const fn_signatures: SigLine[] = [
[() => !runtimeHelpers.emscriptenBuildOptions.enableAotProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]],
[() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_browser", "void", ["string"]],
[() => !runtimeHelpers.emscriptenBuildOptions.enableLogProfiler, "mono_wasm_profiler_init_log", "void", ["string"]],
[true, "mono_wasm_profiler_init_browser", "void", ["number"]],
[false, "mono_wasm_exec_regression", "number", ["number", "string"]],
[false, "mono_wasm_invoke_jsexport", "void", ["number", "number"]],
[true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]],
Expand Down Expand Up @@ -130,6 +129,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_end_catch", "void", []],
[true, "mono_interp_pgo_load_table", "number", ["number", "number"]],
[true, "mono_interp_pgo_save_table", "number", ["number", "number"]],
[() => !runtimeHelpers.emscriptenBuildOptions.enablePerfTracing, "mono_jiterp_prof_enter", "void", ["number", "number"]],
[() => !runtimeHelpers.emscriptenBuildOptions.enablePerfTracing, "mono_jiterp_prof_samplepoint", "void", ["number", "number"]],
[() => !runtimeHelpers.emscriptenBuildOptions.enablePerfTracing, "mono_jiterp_prof_leave", "void", ["number", "number"]],

...threading_cwraps,
];
Expand Down
1 change: 1 addition & 0 deletions src/mono/browser/runtime/es6/dotnet.es6.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function injectDependencies() {
`wasmEnableEH: ${WASM_ENABLE_EH ? "true" : "false"},` +
`enableAotProfiler: ${ENABLE_AOT_PROFILER ? "true" : "false"}, ` +
`enableBrowserProfiler: ${ENABLE_BROWSER_PROFILER ? "true" : "false"}, ` +
`enablePerfTracing: ${ENABLE_BROWSER_PROFILER ? "true" : "false"}, ` +
`enableLogProfiler: ${ENABLE_LOG_PROFILER ? "true" : "false"}, ` +
`runAOTCompilation: ${RUN_AOT_COMPILATION ? "true" : "false"}, ` +
`wasmEnableThreads: ${USE_PTHREADS ? "true" : "false"}, ` +
Expand Down
6 changes: 3 additions & 3 deletions src/mono/browser/runtime/exports-binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { mono_wasm_resolve_or_reject_promise } from "./marshal-to-js";
import { mono_wasm_schedule_timer, schedule_background_exec } from "./scheduling";
import { mono_wasm_asm_loaded } from "./startup";
import { mono_log_warn, mono_wasm_console_clear, mono_wasm_trace_logger } from "./logging";
import { mono_wasm_profiler_leave, mono_wasm_profiler_enter } from "./profiler";
import { mono_wasm_profiler_record, mono_wasm_profiler_now } from "./profiler";
import { mono_wasm_browser_entropy } from "./crypto";
import { mono_wasm_cancel_promise } from "./cancelable-promise";

Expand Down Expand Up @@ -70,8 +70,8 @@ export const mono_wasm_imports = [
mono_interp_flush_jitcall_queue,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
mono_wasm_profiler_now,
mono_wasm_profiler_record,

// driver.c
mono_wasm_trace_logger,
Expand Down
21 changes: 21 additions & 0 deletions src/mono/browser/runtime/jiterpreter-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,27 @@ export const _now = (globalThis.performance && globalThis.performance.now)

let scratchBuffer: NativePointer = <any>0;

export function append_profiler_event (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) {
let event_name:string;
switch (opcode) {
case MintOpcode.MINT_PROF_ENTER:
event_name = "prof_enter";
break;
case MintOpcode.MINT_PROF_SAMPLEPOINT:
event_name = "prof_samplepoint";
break;
case MintOpcode.MINT_PROF_EXIT:
case MintOpcode.MINT_PROF_EXIT_VOID:
event_name = "prof_leave";
break;
default:
throw new Error(`Unimplemented profiler event ${opcode}`);
}
builder.local("frame");
builder.i32_const(ip);
builder.callImport(event_name);
}

export function append_safepoint (builder: WasmBuilder, ip: MintOpcodePtr) {
// safepoints are never triggered in a single-threaded build
if (!WasmEnableThreads)
Expand Down
9 changes: 8 additions & 1 deletion src/mono/browser/runtime/jiterpreter-trace-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
try_append_memmove_fast, getOpcodeTableValue,
getMemberOffset, isZeroPageReserved, CfgBranchType,
append_safepoint, modifyCounter, simdFallbackCounters,
append_profiler_event,
} from "./jiterpreter-support";
import {
sizeOfDataItem, sizeOfV128, sizeOfStackval,
Expand Down Expand Up @@ -1415,9 +1416,15 @@ export function generateWasmBody (
}

case MintOpcode.MINT_RETHROW:
ip = abort;
break;

// call C
case MintOpcode.MINT_PROF_ENTER:
case MintOpcode.MINT_PROF_SAMPLEPOINT:
case MintOpcode.MINT_PROF_EXIT:
case MintOpcode.MINT_PROF_EXIT_VOID:
ip = abort;
append_profiler_event(builder, ip, opcode);
break;

// Generating code for these is kind of complex due to the intersection of JS and int64,
Expand Down
30 changes: 30 additions & 0 deletions src/mono/browser/runtime/jiterpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ function getTraceImports () {
if (nullCheckValidation)
traceImports.push(importDef("notnull", assert_not_null));

if (runtimeHelpers.emscriptenBuildOptions.enablePerfTracing) {
traceImports.push(importDef("prof_enter", getRawCwrap("mono_jiterp_prof_enter")));
traceImports.push(importDef("prof_samplepoint", getRawCwrap("mono_jiterp_prof_samplepoint")));
traceImports.push(importDef("prof_leave", getRawCwrap("mono_jiterp_prof_leave")));
}

const pushMathOps = (list: string[], type: string) => {
for (let i = 0; i < list.length; i++) {
const mop = list[i];
Expand Down Expand Up @@ -579,6 +585,30 @@ function initialize_builder (builder: WasmBuilder) {
},
WasmValtype.void, true
);
builder.defineType(
"prof_enter",
{
"frame": WasmValtype.i32,
"ip": WasmValtype.i32,
},
WasmValtype.void, true
);
builder.defineType(
"prof_samplepoint",
{
"frame": WasmValtype.i32,
"ip": WasmValtype.i32,
},
WasmValtype.void, true
);
builder.defineType(
"prof_leave",
{
"frame": WasmValtype.i32,
"ip": WasmValtype.i32,
},
WasmValtype.void, true
);
builder.defineType(
"hashcode",
{
Expand Down
38 changes: 19 additions & 19 deletions src/mono/browser/runtime/profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ export function mono_wasm_init_browser_profiler (options: BrowserProfilerOptions
mono_assert(runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "Browser profiler is not enabled, please use <WasmProfilers>browser;</WasmProfilers> in your project file.");
if (options == null)
options = {};
const arg = "browser:";
let arg = "browser:";
if (typeof options.callSpec === "string") {
arg += `callspec=${options.callSpec}`;
}
if (typeof options.sampleIntervalMs === "number") {
arg += `interval=${options.sampleIntervalMs}`;
}
cwraps.mono_wasm_profiler_init_browser(arg);
}

Expand Down Expand Up @@ -82,26 +88,20 @@ export function endMeasure (start: TimeStamp, block: string, id?: string) {
}
}

const stackFrames: number[] = [];
export function mono_wasm_profiler_enter (): void {
if (runtimeHelpers.enablePerfMeasure) {
stackFrames.push(globalThis.performance.now());
}
export function mono_wasm_profiler_now (): number {
return globalThis.performance.now();
}

const methodNames: Map<number, string> = new Map();
export function mono_wasm_profiler_leave (method: MonoMethod): void {
if (runtimeHelpers.enablePerfMeasure) {
const start = stackFrames.pop();
const options = ENVIRONMENT_IS_WEB
? { start: start }
: { startTime: start };
let methodName = methodNames.get(method as any);
if (!methodName) {
const chars = cwraps.mono_wasm_method_get_name(method);
methodName = utf8ToString(chars);
methodNames.set(method as any, methodName);
}
globalThis.performance.measure(methodName, options);
export function mono_wasm_profiler_record (method: MonoMethod, start: number): void {
const options = ENVIRONMENT_IS_WEB
? { start: start }
: { startTime: start };
let methodName = methodNames.get(method as any);
if (!methodName) {
const chars = cwraps.mono_wasm_method_get_name(method);
methodName = utf8ToString(chars);
methodNames.set(method as any, methodName);
}
globalThis.performance.measure(methodName, options);
}
3 changes: 3 additions & 0 deletions src/mono/browser/runtime/types/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ export type AOTProfilerOptions = {
}

export type BrowserProfilerOptions = {
sampleIntervalMs?: number, // default: 1000
callSpec?: number, // see callspec in https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md#trace-monovm-profiler-events-during-startup
}

export type LogProfilerOptions = {
Expand All @@ -283,6 +285,7 @@ export type EmscriptenBuildOptions = {
enableAotProfiler: boolean,
enableBrowserProfiler: boolean,
enableLogProfiler: boolean,
enablePerfTracing: boolean,
runAOTCompilation: boolean,
wasmEnableThreads: boolean,
gitHash: string,
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/metadata/jit-icall-reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ MONO_JIT_ICALL (mono_ppc_throw_exception) \
MONO_JIT_ICALL (mono_profiler_raise_exception_clause) \
MONO_JIT_ICALL (mono_profiler_raise_gc_allocation) \
MONO_JIT_ICALL (mono_profiler_raise_method_enter) \
MONO_JIT_ICALL (mono_profiler_raise_method_samplepoint) \
MONO_JIT_ICALL (mono_profiler_raise_method_leave) \
MONO_JIT_ICALL (mono_profiler_raise_method_tail_call) \
MONO_JIT_ICALL (mono_resolve_generic_virtual_call) \
Expand Down Expand Up @@ -298,6 +299,7 @@ MONO_JIT_ICALL (mono_throw_platform_not_supported) \
MONO_JIT_ICALL (mono_throw_invalid_program) \
MONO_JIT_ICALL (mono_throw_type_load) \
MONO_JIT_ICALL (mono_trace_enter_method) \
MONO_JIT_ICALL (mono_trace_samplepoint_method) \
MONO_JIT_ICALL (mono_trace_leave_method) \
MONO_JIT_ICALL (mono_trace_tail_method) \
MONO_JIT_ICALL (mono_upgrade_remote_class_wrapper) \
Expand Down
90 changes: 55 additions & 35 deletions src/mono/mono/mini/interp/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3891,6 +3891,24 @@ interp_ldvirtftn_delegate (gpointer arg, MonoDelegate *del)
return imethod_to_ftnptr (imethod, need_unbox);
}

#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))) { \
MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);\
prof_ctx->interp_frame = frame;\
prof_ctx->method = frame->imethod->method; \
if (!is_void) \
prof_ctx->return_value = frame->retval; \
if (flag & TRACING_FLAG) \
mono_trace_ ## name_lower ## _method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); \
if (flag & PROFILING_FLAG) \
MONO_PROFILER_RAISE (method_ ## name_lower, (frame->imethod->method, prof_ctx)); \
g_free (prof_ctx); \
} else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_ ## name_lower)) { \
MONO_PROFILER_RAISE (method_ ## name_lower, (frame->imethod->method, NULL)); \
}


/*
* If CLAUSE_ARGS is non-null, start executing from it.
* The ERROR argument is used to avoid declaring an error object for every interp frame, its not used
Expand Down Expand Up @@ -7610,24 +7628,19 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
MINT_IN_CASE(MINT_PROF_ENTER) {
guint16 flag = ip [1];
ip += 2;

if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_enter) &&
(frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT))) {
MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;
// FIXME push/pop LMF
if (flag & TRACING_FLAG)
mono_trace_enter_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx);
if (flag & PROFILING_FLAG)
MONO_PROFILER_RAISE (method_enter, (frame->imethod->method, prof_ctx));
g_free (prof_ctx);
} else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_enter)) {
MONO_PROFILER_RAISE (method_enter, (frame->imethod->method, NULL));
}
gboolean is_void = TRUE;
// FIXME push/pop LMF
INTERP_PROFILER_RAISE(enter, ENTER);
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_PROF_SAMPLEPOINT) {
guint16 flag = ip [1];
ip += 2;
gboolean is_void = TRUE;
// FIXME push/pop LMF
INTERP_PROFILER_RAISE(samplepoint, SAMPLEPOINT);
MINT_IN_BREAK;
}

MINT_IN_CASE(MINT_PROF_EXIT)
MINT_IN_CASE(MINT_PROF_EXIT_VOID) {
gboolean is_void = ip [0] == MINT_PROF_EXIT_VOID;
Expand All @@ -7640,24 +7653,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
else
frame->retval [0] = LOCAL_VAR (ip [1], stackval);
}

if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_leave) &&
(frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT))) {
MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;
if (!is_void)
prof_ctx->return_value = frame->retval;
// FIXME push/pop LMF
if (flag & TRACING_FLAG)
mono_trace_leave_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx);
if (flag & PROFILING_FLAG)
MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, prof_ctx));
g_free (prof_ctx);
} else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_enter)) {
MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, NULL));
}

INTERP_PROFILER_RAISE(leave, LEAVE);
frame_data_allocator_pop (&context->data_stack, frame);
goto exit_frame;
}
Expand Down Expand Up @@ -9126,7 +9122,7 @@ mono_jiterp_interp_entry (JiterpEntryData *_data, void *res)
int params_size = get_arg_offset_fast (header.rmethod, NULL, header.params_count);
// g_printf ("jiterp_interp_entry: rmethod=%d, params_count=%d, params_size=%d\n", header.rmethod, header.params_count, params_size);
header.context->stack_pointer = (guchar*)ALIGN_TO ((guchar*)sp + params_size, MINT_STACK_ALIGNMENT);
;

g_assert (header.context->stack_pointer < header.context->stack_end);

MONO_ENTER_GC_UNSAFE;
Expand Down Expand Up @@ -9162,6 +9158,30 @@ mono_jiterp_get_polling_required_address ()
return &mono_polling_required;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_prof_enter (InterpFrame *frame, guint16 *ip)
{
gboolean is_void = TRUE;
guint16 flag = ip [1];
INTERP_PROFILER_RAISE(enter, ENTER);
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_prof_samplepoint (InterpFrame *frame, guint16 *ip)
{
guint16 flag = ip [1];
gboolean is_void = TRUE;
INTERP_PROFILER_RAISE(samplepoint, SAMPLEPOINT);
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_prof_leave (InterpFrame *frame, guint16 *ip)
{
gboolean is_void = ip [0] == MINT_PROF_EXIT_VOID;
guint16 flag = is_void ? ip [1] : ip [2];
INTERP_PROFILER_RAISE(leave, LEAVE);
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_do_safepoint (InterpFrame *frame, guint16 *ip)
{
Expand Down
9 changes: 9 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ mono_jiterp_get_polling_required_address (void);
void
mono_jiterp_do_safepoint (InterpFrame *frame, guint16 *ip);

void
mono_jiterp_prof_enter (InterpFrame *frame, guint16 *ip);

void
mono_jiterp_prof_samplepoint (InterpFrame *frame, guint16 *ip);

void
mono_jiterp_prof_leave (InterpFrame *frame, guint16 *ip);

void
mono_jiterp_interp_entry (JiterpEntryData *_data, void *res);

Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/interp/mintops.def
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,7 @@ OPDEF(MINT_MINF, "min_f", 4, 1, 2, MintOpNoArgs)
OPDEF(MINT_MAXF, "max_f", 4, 1, 2, MintOpNoArgs)

OPDEF(MINT_PROF_ENTER, "prof_enter", 2, 0, 0, MintOpShortInt)
OPDEF(MINT_PROF_SAMPLEPOINT, "prof_samplepoint", 2, 0, 0, MintOpShortInt)
OPDEF(MINT_PROF_EXIT, "prof_exit", 5, 0, 1, MintOpShortAndInt)
OPDEF(MINT_PROF_EXIT_VOID, "prof_exit_void", 2, 0, 0, MintOpNoArgs)
OPDEF(MINT_PROF_COVERAGE_STORE, "prof_coverage_store", 5, 0, 0, MintOpLongInt)
Expand Down
12 changes: 12 additions & 0 deletions src/mono/mono/mini/interp/transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,18 @@ handle_branch (TransformData *td, int long_op, int offset)
g_assert_not_reached ();
/* Add exception checkpoint or safepoint for backward branches */
if (offset < 0) {

InterpMethod *rtm = td->rtm;
guint16 samplepoint_profiling = 0;
if (mono_jit_trace_calls != NULL && mono_trace_eval (rtm->method))
samplepoint_profiling |= TRACING_FLAG;
if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT)
samplepoint_profiling |= PROFILING_FLAG;
if (samplepoint_profiling) {
interp_add_ins (td, MINT_PROF_SAMPLEPOINT);
td->last_ins->data [0] = samplepoint_profiling;
}

if (mono_threads_are_safepoints_enabled ())
interp_add_ins (td, MINT_SAFEPOINT);
}
Expand Down
Loading
Loading