From 3417db1b4289cd77d3b79dfff951673cb1afbf5a Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Feb 2025 14:31:25 +0100 Subject: [PATCH 01/25] wip --- src/mono/browser/runtime/exports-binding.ts | 3 +- src/mono/browser/runtime/profiler.ts | 84 +++++++++++++++---- src/mono/browser/runtime/types/internal.ts | 1 + src/mono/mono/metadata/jit-icall-reg.h | 2 + src/mono/mono/mini/calls.c | 1 + src/mono/mono/mini/interp/interp.c | 22 ++++- src/mono/mono/mini/interp/mintops.def | 1 + src/mono/mono/mini/interp/transform.c | 22 +++++ src/mono/mono/mini/method-to-ir.c | 13 ++- src/mono/mono/mini/mini-profiler.c | 29 +++++++ src/mono/mono/mini/mini-runtime.c | 2 + src/mono/mono/mini/mini.c | 2 +- src/mono/mono/mini/mini.h | 1 + src/mono/mono/mini/trace.c | 16 +++- src/mono/mono/mini/trace.h | 4 + src/mono/mono/profiler/browser.c | 19 ++++- .../mono/metadata/details/profiler-types.h | 4 + .../public/mono/metadata/profiler-events.h | 1 + 18 files changed, 203 insertions(+), 24 deletions(-) diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 03879c4381de0f..7ae33aed064486 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -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_leave, mono_wasm_profiler_enter, mono_wasm_profiler_samplepoint } from "./profiler"; import { mono_wasm_browser_entropy } from "./crypto"; import { mono_wasm_cancel_promise } from "./cancelable-promise"; @@ -72,6 +72,7 @@ export const mono_wasm_imports = [ mono_wasm_profiler_enter, mono_wasm_profiler_leave, + mono_wasm_profiler_samplepoint, // driver.c mono_wasm_trace_logger, diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 0829b3ec71a66a..4f693dae1a6967 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -30,6 +30,9 @@ export function mono_wasm_init_browser_profiler (options: BrowserProfilerOptions mono_assert(runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "Browser profiler is not enabled, please use browser; in your project file."); if (options == null) options = {}; + if (typeof options.sampleIntervalMs === "number") { + desiredSampleIntervalMs = options.sampleIntervalMs!; + } const arg = "browser:"; cwraps.mono_wasm_profiler_init_browser(arg); } @@ -82,26 +85,79 @@ export function endMeasure (start: TimeStamp, block: string, id?: string) { } } +let desiredSampleIntervalMs = 10;// ms +let lastSampleTime = 0; +let skipsPerPeriod = 1; +let sampleSkipCounter = 0; const stackFrames: number[] = []; +const stackFramesSkip: boolean[] = []; + export function mono_wasm_profiler_enter (): void { - if (runtimeHelpers.enablePerfMeasure) { - stackFrames.push(globalThis.performance.now()); + if (!runtimeHelpers.enablePerfMeasure) { + return; } + + stackFrames.push(globalThis.performance.now()); + stackFramesSkip.push(true); } const methodNames: Map = 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); + if (!runtimeHelpers.enablePerfMeasure) { + return; + } + + const start = stackFrames.pop(); + const skip = stackFramesSkip.pop(); + + // no sample point and skip also now + if (skip && skipSample()) { + return; + } + + // propagate !skip to parent + stackFramesSkip[stackFramesSkip.length - 1] = false; + + 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_samplepoint (): void { + if (!runtimeHelpers.enablePerfMeasure || skipSample()) { + return; + } + + // mark the current frame to record the stack frame + stackFramesSkip[stackFramesSkip.length - 1] = false; +} + +function skipSample ():boolean { + sampleSkipCounter++; + if (sampleSkipCounter < skipsPerPeriod) { + return true; + } + + // timer resolution in non-isolated contexts: 100 microseconds (decimal number) + const now = globalThis.performance.now(); + + if (desiredSampleIntervalMs > 0) { + // recalculate ideal number of skips per period + const msSinceLastSample = now - lastSampleTime; + const skipsPerMs = skipsPerPeriod / msSinceLastSample; + const newSkipsPerPeriod = (skipsPerMs * desiredSampleIntervalMs) | 0; + skipsPerPeriod = (newSkipsPerPeriod + skipsPerPeriod) / 2; + lastSampleTime = now; + } else { + skipsPerPeriod = 0; + } + + return false; } diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 54f11256806273..53c61203df5d80 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -259,6 +259,7 @@ export type AOTProfilerOptions = { } export type BrowserProfilerOptions = { + sampleIntervalMs?: number, // default: 1000 } export type LogProfilerOptions = { diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index e412fdd5448dca..368f6682228197 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -249,6 +249,7 @@ MONO_JIT_ICALL (mono_object_isinst_with_cache) \ 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_samplepoint) \ MONO_JIT_ICALL (mono_profiler_raise_method_enter) \ MONO_JIT_ICALL (mono_profiler_raise_method_leave) \ MONO_JIT_ICALL (mono_profiler_raise_method_tail_call) \ @@ -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) \ diff --git a/src/mono/mono/mini/calls.c b/src/mono/mono/mini/calls.c index f150bc4d308174..ec01deda0cc16f 100644 --- a/src/mono/mono/mini/calls.c +++ b/src/mono/mono/mini/calls.c @@ -178,6 +178,7 @@ mini_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, } if (tailcall) { + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, target); mini_emit_tailcall_parameters (cfg, sig); MONO_INST_NEW_CALL (cfg, call, calli ? OP_TAILCALL_REG : virtual_ ? OP_TAILCALL_MEMBASE : OP_TAILCALL); diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index a83fd3ac4e3380..05f53abac492d4 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -7607,6 +7607,26 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 3; MINT_IN_BREAK; } + MINT_IN_CASE(MINT_PROF_SAMPLEPOINT) { + guint16 flag = ip [1]; + ip += 2; + + if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_samplepoint) && + (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_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_samplepoint_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); + if (flag & PROFILING_FLAG) + MONO_PROFILER_RAISE (method_samplepoint, (frame->imethod->method, prof_ctx)); + g_free (prof_ctx); + } else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_samplepoint)) { + MONO_PROFILER_RAISE (method_samplepoint, (frame->imethod->method, NULL)); + } + MINT_IN_BREAK; + } MINT_IN_CASE(MINT_PROF_ENTER) { guint16 flag = ip [1]; ip += 2; @@ -7654,7 +7674,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; 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)) { + } else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_leave)) { MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, NULL)); } diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 43424be305ede3..0b7d0125a88ece 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -793,6 +793,7 @@ OPDEF(MINT_ABSF, "abs_f", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_MINF, "min_f", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_MAXF, "max_f", 4, 1, 2, MintOpNoArgs) +OPDEF(MINT_PROF_SAMPLEPOINT, "prof_samplepoint", 2, 0, 0, MintOpShortInt) OPDEF(MINT_PROF_ENTER, "prof_enter", 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) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 53ca6d2a10804a..ac8019769dd746 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -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); } @@ -5327,6 +5339,16 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = enter_profiling; } + guint16 samplepoint_profiling = 0; + if (mono_jit_trace_calls != NULL && mono_trace_eval (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 safepoints are required by default, always check for polling, * without emitting new instructions. This optimizes method entry in diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index d966d0ec0de6bf..de64fbe694bd97 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -7458,6 +7458,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->gshared && mono_method_check_context_used (cmethod)) GENERIC_SHARING_FAILURE (CEE_JMP); + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, cmethod); fsig = mono_method_signature_internal (cmethod); @@ -8310,6 +8311,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b for (i = 0; i < n; ++i) EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]); + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, cmethod); MONO_INST_NEW (cfg, ins, OP_BR); @@ -8691,8 +8693,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; } case MONO_CEE_RET: - if (!detached_before_ret) + if (!detached_before_ret) { + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL); + } g_assert (!method_does_not_return (method)); @@ -11223,6 +11227,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * Emit instrumentation code before linking the basic blocks below as this * will alter cfg->cbb. */ + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_call_finally (cfg, header, ip, leave->index, clause); tblock = cfg->cil_offset_to_bb [clause->handler_offset]; @@ -11317,6 +11322,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { if (token == MONO_JIT_ICALL_mono_threads_detach_coop) { /* can't emit profiling code after a detach, so emit it now */ + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, NULL); detached_before_ret = TRUE; } @@ -11484,8 +11490,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (sp != stack_start) UNVERIFIED; - if (!detached_before_ret) + if (!detached_before_ret) { + mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, sp [0]); + } MONO_INST_NEW (cfg, ins, OP_BR); ins->inst_target_bb = end_bblock; @@ -12351,6 +12359,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* emit profiler enter code after a jit attach if there is one */ cfg->cbb = init_localsbb2; mini_profiler_emit_enter (cfg); + mini_profiler_emit_samplepoint (cfg); cfg->cbb = init_localsbb; if (seq_points) { diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index 74f997f5bf5dee..28052a546db629 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -101,6 +101,34 @@ mini_profiler_emit_enter (MonoCompile *cfg) mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs); } +void +mini_profiler_emit_samplepoint (MonoCompile *cfg) +{ + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); + + if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + return; + + if (cfg->current_method != cfg->method) + return; + + MonoInst *iargs [3]; + + EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); + EMIT_NEW_PCONST (cfg, iargs [1], NULL); + + if (MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) + iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); + else + EMIT_NEW_PCONST (cfg, iargs [2], NULL); + + /* void mono_profiler_raise_method_samplepoint (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ + if (trace) + mono_emit_jit_icall (cfg, mono_trace_samplepoint_method, iargs); + else + mono_emit_jit_icall (cfg, mono_profiler_raise_method_samplepoint, iargs); +} + void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) { @@ -372,3 +400,4 @@ mini_profiler_context_free_buffer (void *buffer) { g_free (buffer); } + diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index bcd7754da660a6..92e6cfa979e88a 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4939,12 +4939,14 @@ register_icalls (void) * the wrapper would call the icall which would call the wrapper and * so on. */ + register_icall (mono_profiler_raise_method_samplepoint, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_enter, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_leave, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_tail_call, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_exception_clause, mono_icall_sig_void_ptr_int_int_object, TRUE); register_icall (mono_trace_enter_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE); + register_icall (mono_trace_samplepoint_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE); register_icall (mono_trace_leave_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE); register_icall (mono_trace_tail_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE); g_assert (mono_get_lmf_addr == mono_tls_get_lmf_addr); diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 63d89b3f28a561..0a30146a651f3b 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3385,7 +3385,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts if (trace) cfg->prof_flags = (MonoProfilerCallInstrumentationFlags)( MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT | - MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT); + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT); /* The debugger has no liveness information, so avoid sharing registers/stack slots */ if (mini_debug_options.mdb_optimizations || MONO_CFG_PROFILE_CALL_CONTEXT (cfg)) { diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 97196e7e125cb4..a9ee123e32d7c8 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2124,6 +2124,7 @@ mono_bb_last_inst (MonoBasicBlock *bb, int filter) /* profiler support */ void mini_add_profiler_argument (const char *desc); +void mini_profiler_emit_samplepoint (MonoCompile *cfg); void mini_profiler_emit_enter (MonoCompile *cfg); void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret); void mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target); diff --git a/src/mono/mono/mini/trace.c b/src/mono/mono/mini/trace.c index c0b175fa7055ce..bec8f13fa45421 100644 --- a/src/mono/mono/mini/trace.c +++ b/src/mono/mono/mini/trace.c @@ -139,8 +139,22 @@ frame_kind (MonoJitInfo *ji) return 'c'; } +static void mono_trace_enter_method_impl (const char *prefix, MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx); + +void +mono_trace_samplepoint_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) +{ + mono_trace_enter_method_impl ("SAMPLEPOINT:%c %s(", method, ji, ctx); +} + void mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) +{ + mono_trace_enter_method_impl ("ENTER:%c %s(", method, ji, ctx); +} + +static void +mono_trace_enter_method_impl (const char *prefix, MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) { int i; MonoClass *klass; @@ -162,7 +176,7 @@ mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallCo if (!ji) ji = mini_jit_info_table_find ((char *)MONO_RETURN_ADDRESS ()); - printf ("ENTER:%c %s(", frame_kind (ji), fname); + printf (prefix, frame_kind (ji), fname); g_free (fname); sig = mono_method_signature_internal (method); diff --git a/src/mono/mono/mini/trace.h b/src/mono/mono/mini/trace.h index a286e574e1f4b4..5b07a74a087bb5 100644 --- a/src/mono/mono/mini/trace.h +++ b/src/mono/mono/mini/trace.h @@ -12,6 +12,10 @@ ICALL_EXPORT void mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx); +ICALL_EXPORT +void +mono_trace_samplepoint_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx); + ICALL_EXPORT void mono_trace_leave_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx); diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index 2882f0188ba59b..5b6dd5a26f303b 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -38,12 +38,21 @@ mono_wasm_profiler_enter (); void mono_wasm_profiler_leave (MonoMethod *method); +void +mono_wasm_profiler_samplepoint (); + static void method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { mono_wasm_profiler_enter (); } +static void +method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) +{ + mono_wasm_profiler_samplepoint (); +} + static void method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { @@ -68,10 +77,11 @@ static MonoProfilerCallInstrumentationFlags method_filter (MonoProfiler *prof, MonoMethod *method) { // TODO filter by namespace ? - return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | - MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | - MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | - MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; + return MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT | + MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; } @@ -95,6 +105,7 @@ mono_profiler_init_browser (const char *desc) #ifdef HOST_WASM // install this only in production run, not in AOT run + mono_profiler_set_method_samplepoint_callback (handle, method_samplepoint); mono_profiler_set_method_enter_callback (handle, method_enter); mono_profiler_set_method_leave_callback (handle, method_leave); mono_profiler_set_method_tail_call_callback (handle, tail_call); diff --git a/src/native/public/mono/metadata/details/profiler-types.h b/src/native/public/mono/metadata/details/profiler-types.h index 966459e58e3337..1a1c419e5d2097 100644 --- a/src/native/public/mono/metadata/details/profiler-types.h +++ b/src/native/public/mono/metadata/details/profiler-types.h @@ -77,6 +77,10 @@ typedef enum { * Instrument exceptional method exits. */ MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE = 1 << 6, + /** + * Instrument method samplepoints with context. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT = 1 << 7, } MonoProfilerCallInstrumentationFlags; typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFilterCallback) (MonoProfiler *prof, MonoMethod *method); diff --git a/src/native/public/mono/metadata/profiler-events.h b/src/native/public/mono/metadata/profiler-events.h index 20300bff65f1c2..2ae8c3f15c6ea2 100644 --- a/src/native/public/mono/metadata/profiler-events.h +++ b/src/native/public/mono/metadata/profiler-events.h @@ -77,6 +77,7 @@ MONO_PROFILER_EVENT_2(method_exception_leave, MethodExceptionLeave, MonoMethod * MONO_PROFILER_EVENT_1(method_free, MethodFree, MonoMethod *, method) MONO_PROFILER_EVENT_1(method_begin_invoke, MethodBeginInvoke, MonoMethod *, method) MONO_PROFILER_EVENT_1(method_end_invoke, MethodEndInvoke, MonoMethod *, method) +MONO_PROFILER_EVENT_2(method_samplepoint, MethodSamplepoint, MonoMethod *, method, MonoProfilerCallContext *, context) MONO_PROFILER_EVENT_1(exception_throw, ExceptionThrow, MonoObject *, exception) MONO_PROFILER_EVENT_4(exception_clause, ExceptionClause, MonoMethod *, method, uint32_t, index, MonoExceptionEnum, type, MonoObject *, exception) From 449be731007ee05c81ed04de3f665c442b804054 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Feb 2025 15:31:21 +0100 Subject: [PATCH 02/25] fix calculation --- src/mono/browser/runtime/profiler.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 4f693dae1a6967..218c556b6124f4 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -131,11 +131,14 @@ export function mono_wasm_profiler_leave (method: MonoMethod): void { } export function mono_wasm_profiler_samplepoint (): void { - if (!runtimeHelpers.enablePerfMeasure || skipSample()) { + if (!runtimeHelpers.enablePerfMeasure) { + return; + } + if (!stackFramesSkip[stackFramesSkip.length - 1] || skipSample()) { return; } - // mark the current frame to record the stack frame + // mark the current frame to not skip stackFramesSkip[stackFramesSkip.length - 1] = false; } @@ -148,16 +151,17 @@ function skipSample ():boolean { // timer resolution in non-isolated contexts: 100 microseconds (decimal number) const now = globalThis.performance.now(); - if (desiredSampleIntervalMs > 0) { + if (desiredSampleIntervalMs > 0 && lastSampleTime != 0) { // recalculate ideal number of skips per period const msSinceLastSample = now - lastSampleTime; - const skipsPerMs = skipsPerPeriod / msSinceLastSample; - const newSkipsPerPeriod = (skipsPerMs * desiredSampleIntervalMs) | 0; - skipsPerPeriod = (newSkipsPerPeriod + skipsPerPeriod) / 2; - lastSampleTime = now; + const skipsPerMs = sampleSkipCounter / msSinceLastSample; + const newSkipsPerPeriod = (skipsPerMs * desiredSampleIntervalMs); + skipsPerPeriod = ((newSkipsPerPeriod + skipsPerPeriod) / 2) | 0; } else { skipsPerPeriod = 0; } + lastSampleTime = now; + sampleSkipCounter = 0; return false; } From e7ae514484a7a19175cdd446598e839546d01529 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Feb 2025 15:40:34 +0100 Subject: [PATCH 03/25] sliding --- src/mono/browser/runtime/profiler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 218c556b6124f4..08ba356af8f183 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -87,6 +87,7 @@ export function endMeasure (start: TimeStamp, block: string, id?: string) { let desiredSampleIntervalMs = 10;// ms let lastSampleTime = 0; +let prevskipsPerPeriod = 1; let skipsPerPeriod = 1; let sampleSkipCounter = 0; const stackFrames: number[] = []; @@ -156,7 +157,8 @@ function skipSample ():boolean { const msSinceLastSample = now - lastSampleTime; const skipsPerMs = sampleSkipCounter / msSinceLastSample; const newSkipsPerPeriod = (skipsPerMs * desiredSampleIntervalMs); - skipsPerPeriod = ((newSkipsPerPeriod + skipsPerPeriod) / 2) | 0; + skipsPerPeriod = ((newSkipsPerPeriod + sampleSkipCounter + prevskipsPerPeriod) / 3) | 0; + prevskipsPerPeriod = sampleSkipCounter; } else { skipsPerPeriod = 0; } From b5df7cd39cdbad50541be746a799cf504a2209af Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Feb 2025 16:05:07 +0100 Subject: [PATCH 04/25] Timing-Allow-Origin header --- .../wasm/browser-advanced/Wasm.Advanced.Sample.csproj | 2 +- src/mono/sample/wasm/browser-advanced/main.js | 6 +++++- src/mono/sample/wasm/simple-server/Program.cs | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index fee308d1230f9d..fec100d2053651 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -11,7 +11,7 @@ -s USE_CLOSURE_COMPILER=1 -s LEGACY_GL_EMULATION=1 -lGL -lSDL -lidbfs.js - <_ServeHeaders>$(_ServeHeaders) -h "Content-Security-Policy: default-src 'self' 'wasm-unsafe-eval'" + <_ServeHeaders>$(_ServeHeaders) -h "Content-Security-Policy: default-src 'self' 'wasm-unsafe-eval'" -h "Timing-Allow-Origin: *" -h "Cross-Origin-Opener-Policy: same-origin" -h "Cross-Origin-Embedder-Policy: require-corp" browser; diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index fe0331693d6de6..e28be2442aa138 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -15,6 +15,8 @@ let testAbort = true; let testError = true; try { + console.log(`crossOriginIsolated: ${globalThis.crossOriginIsolated}`); + const originalFetch = globalThis.fetch; globalThis.fetch = (url, fetchArgs) => { console.log("fetching " + url); @@ -50,7 +52,9 @@ try { console.log('user code Module.onConfigLoaded'); // config is loaded and could be tweaked before the rest of the runtime startup sequence config.environmentVariables["MONO_LOG_LEVEL"] = "debug"; - config.browserProfilerOptions = {}; + config.browserProfilerOptions = { + sampleIntervalMs: 1 + }; }, preInit: () => { console.log('user code Module.preInit'); }, preRun: () => { console.log('user code Module.preRun'); }, diff --git a/src/mono/sample/wasm/simple-server/Program.cs b/src/mono/sample/wasm/simple-server/Program.cs index e978ef4922efa3..db53032b39b480 100644 --- a/src/mono/sample/wasm/simple-server/Program.cs +++ b/src/mono/sample/wasm/simple-server/Program.cs @@ -356,6 +356,7 @@ private async void ServeAsync(HttpListenerContext context) // context.Response.AppendHeader("cache-control", "public, max-age=31536000"); context.Response.AppendHeader("Cross-Origin-Embedder-Policy", "require-corp"); context.Response.AppendHeader("Cross-Origin-Opener-Policy", "same-origin"); + context.Response.AppendHeader("Timing-Allow-Origin", "*"); context.Response.AppendHeader("ETag", fc.hash); // test download re-try From 8134a3c69619e363a3a98f932c7ec61105e3b4a9 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Feb 2025 19:37:00 +0100 Subject: [PATCH 05/25] more --- src/mono/browser/runtime/profiler.ts | 5 +- src/mono/browser/runtime/types/internal.ts | 1 + src/mono/mono/profiler/browser.c | 52 ++++++++++++++++++- .../browser-advanced/ILLink.Descriptors.xml | 8 +++ .../Wasm.Advanced.Sample.csproj | 9 +++- src/mono/sample/wasm/browser-advanced/main.js | 3 +- 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 08ba356af8f183..0fe548cf8f5d92 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -33,7 +33,10 @@ export function mono_wasm_init_browser_profiler (options: BrowserProfilerOptions if (typeof options.sampleIntervalMs === "number") { desiredSampleIntervalMs = options.sampleIntervalMs!; } - const arg = "browser:"; + let arg = "browser:"; + if (typeof options.callSpec === "string") { + arg += `callspec=${options.callSpec}`; + } cwraps.mono_wasm_profiler_init_browser(arg); } diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 53c61203df5d80..41379e25ae1b4e 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -260,6 +260,7 @@ 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 = { diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index 5b6dd5a26f303b..eaf2bc6593609d 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ struct _MonoProfiler { }; static MonoProfiler browser_profiler; +static MonoCallSpec callspec; + #ifdef HOST_WASM @@ -76,7 +79,10 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) static MonoProfilerCallInstrumentationFlags method_filter (MonoProfiler *prof, MonoMethod *method) { - // TODO filter by namespace ? + if (callspec.len > 0 && + !mono_callspec_eval (method, &callspec)) + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + return MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | @@ -88,6 +94,26 @@ method_filter (MonoProfiler *prof, MonoMethod *method) MONO_API void mono_profiler_init_browser (const char *desc); +static gboolean +match_option (const char *arg, const char *opt_name, const char **rval) +{ + if (rval) { + const char *end = strchr (arg, '='); + + *rval = NULL; + if (!end) + return !strcmp (arg, opt_name); + + if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > (ptrdiff_t)strlen (opt_name) + 1) + return FALSE; + *rval = end + 1; + return TRUE; + } else { + //FIXME how should we handle passing a value to an arg that doesn't expect it? + return !strcmp (arg, opt_name); + } +} + /** * mono_profiler_init_browser: * the entry point @@ -95,6 +121,30 @@ mono_profiler_init_browser (const char *desc); void mono_profiler_init_browser (const char *desc) { + // browser: + if (!desc && desc [7] == ':') { + const char *arg = desc + 8; + const char *val; + + if (match_option (arg, "callspec", &val)) { + if (!val) + val = ""; + if (val[0] == '\"') + ++val; + char *spec = g_strdup (val); + size_t speclen = strlen (val); + if (speclen > 0 && spec[speclen - 1] == '\"') + spec[speclen - 1] = '\0'; + char *errstr; + if (!mono_callspec_parse (spec, &callspec, &errstr)) { + mono_profiler_printf_err ("Could not parse callspec '%s': %s", spec, errstr); + g_free (errstr); + mono_callspec_cleanup (&callspec); + } + g_free (spec); + } + } + MonoProfilerHandle handle = mono_profiler_create (&browser_profiler); mono_profiler_set_call_instrumentation_filter_callback (handle, method_filter); diff --git a/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml b/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml index 665eae765f7332..f418cc51be4b32 100644 --- a/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml +++ b/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml @@ -5,4 +5,12 @@ + + + + + + + + diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index fec100d2053651..5e9f768bd3ed12 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -13,10 +13,17 @@ <_ServeHeaders>$(_ServeHeaders) -h "Content-Security-Policy: default-src 'self' 'wasm-unsafe-eval'" -h "Timing-Allow-Origin: *" -h "Cross-Origin-Opener-Policy: same-origin" -h "Cross-Origin-Embedder-Policy: require-corp" - browser; + + browser:callspec=N:Sample; ./ + + + true + false diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index e28be2442aa138..4e8dd182f6109c 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -53,7 +53,8 @@ try { // config is loaded and could be tweaked before the rest of the runtime startup sequence config.environmentVariables["MONO_LOG_LEVEL"] = "debug"; config.browserProfilerOptions = { - sampleIntervalMs: 1 + sampleIntervalMs: 1, + callSpec: "N:Sample" }; }, preInit: () => { console.log('user code Module.preInit'); }, From f86d001cdd0cd8517e219faedeba576737559a7e Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 12 Feb 2025 16:11:17 +0100 Subject: [PATCH 06/25] fix --- src/mono/browser/runtime/cwraps.ts | 1 - src/mono/mono/profiler/browser.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 19a24b08a11b13..f7a7c367e7139a 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -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"]], diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index eaf2bc6593609d..6c20c98b3312a8 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -122,7 +122,7 @@ void mono_profiler_init_browser (const char *desc) { // browser: - if (!desc && desc [7] == ':') { + if (desc && desc [7] == ':') { const char *arg = desc + 8; const char *val; From ea00205fb4c29ac09db98c2ddde0409ee850f6bf Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 12 Feb 2025 17:10:55 +0100 Subject: [PATCH 07/25] todo --- src/mono/browser/runtime/es6/dotnet.es6.lib.js | 1 + src/mono/browser/runtime/types/internal.ts | 1 + src/mono/mono/mini/mini-llvm.c | 2 ++ src/mono/mono/mini/mini.c | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/mono/browser/runtime/es6/dotnet.es6.lib.js b/src/mono/browser/runtime/es6/dotnet.es6.lib.js index ad8b0303bbb12d..7d9275018e0505 100644 --- a/src/mono/browser/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/browser/runtime/es6/dotnet.es6.lib.js @@ -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"}, ` + diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 41379e25ae1b4e..7aa97a5f5c10f5 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -285,6 +285,7 @@ export type EmscriptenBuildOptions = { enableAotProfiler: boolean, enableBrowserProfiler: boolean, enableLogProfiler: boolean, + enablePerfTracing: boolean, runAOTCompilation: boolean, wasmEnableThreads: boolean, gitHash: string, diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 8cc8c986381b52..2d02f942dd68b0 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -13161,6 +13161,8 @@ emit_method_inner (EmitContext *ctx) } LLVMSetLinkage (method, LLVMPrivateLinkage); + // TODO emit samplepoint + mono_llvm_add_func_attr (method, LLVM_ATTR_UW_TABLE); if (cfg->disable_omit_fp) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 0a30146a651f3b..4a6d83b84dc569 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3677,6 +3677,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts MONO_TIME_TRACK (mono_jit_stats.jit_insert_safepoints, insert_safepoints (cfg)); mono_cfg_dump_ir (cfg, "insert_safepoints"); } + + // TODO insert_samplepoints /* after method_to_ir */ if (parts == 1) { From ea8bf7f4baedd216eaf91586a9a52c028e6f1796 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 12 Feb 2025 19:00:10 +0100 Subject: [PATCH 08/25] less --- src/mono/browser/runtime/profiler.ts | 2 +- src/mono/mono/mini/calls.c | 1 - src/mono/mono/mini/interp/transform.c | 10 ---------- src/mono/mono/mini/method-to-ir.c | 7 ------- 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 0fe548cf8f5d92..879cee96d8905f 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -102,7 +102,7 @@ export function mono_wasm_profiler_enter (): void { } stackFrames.push(globalThis.performance.now()); - stackFramesSkip.push(true); + stackFramesSkip.push(skipSample()); } const methodNames: Map = new Map(); diff --git a/src/mono/mono/mini/calls.c b/src/mono/mono/mini/calls.c index ec01deda0cc16f..f150bc4d308174 100644 --- a/src/mono/mono/mini/calls.c +++ b/src/mono/mono/mini/calls.c @@ -178,7 +178,6 @@ mini_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, } if (tailcall) { - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, target); mini_emit_tailcall_parameters (cfg, sig); MONO_INST_NEW_CALL (cfg, call, calli ? OP_TAILCALL_REG : virtual_ ? OP_TAILCALL_MEMBASE : OP_TAILCALL); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index ac8019769dd746..6f1722d58beca5 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -5339,16 +5339,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = enter_profiling; } - guint16 samplepoint_profiling = 0; - if (mono_jit_trace_calls != NULL && mono_trace_eval (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 safepoints are required by default, always check for polling, * without emitting new instructions. This optimizes method entry in diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index de64fbe694bd97..1f919ebde5fa5b 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -7458,7 +7458,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->gshared && mono_method_check_context_used (cmethod)) GENERIC_SHARING_FAILURE (CEE_JMP); - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, cmethod); fsig = mono_method_signature_internal (cmethod); @@ -8311,7 +8310,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b for (i = 0; i < n; ++i) EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]); - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_tail_call (cfg, cmethod); MONO_INST_NEW (cfg, ins, OP_BR); @@ -8694,7 +8692,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } case MONO_CEE_RET: if (!detached_before_ret) { - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL); } @@ -11227,7 +11224,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * Emit instrumentation code before linking the basic blocks below as this * will alter cfg->cbb. */ - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_call_finally (cfg, header, ip, leave->index, clause); tblock = cfg->cil_offset_to_bb [clause->handler_offset]; @@ -11322,7 +11318,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { if (token == MONO_JIT_ICALL_mono_threads_detach_coop) { /* can't emit profiling code after a detach, so emit it now */ - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, NULL); detached_before_ret = TRUE; } @@ -11491,7 +11486,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b UNVERIFIED; if (!detached_before_ret) { - mini_profiler_emit_samplepoint (cfg); mini_profiler_emit_leave (cfg, sp [0]); } @@ -12359,7 +12353,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* emit profiler enter code after a jit attach if there is one */ cfg->cbb = init_localsbb2; mini_profiler_emit_enter (cfg); - mini_profiler_emit_samplepoint (cfg); cfg->cbb = init_localsbb; if (seq_points) { From 07b79f7e37a49534dd5dc8a2255b2a27f0dc0c44 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 12 Feb 2025 22:29:45 +0100 Subject: [PATCH 09/25] fix --- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 7ae3255fac5bee..bb10dc0c456f3b 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -1165,14 +1165,15 @@ void register_aot_modules (void) }} } - {{profilers.Join(writer.NewLine, profiler => - $$$"""" + {{profilers.Join(writer.NewLine, p => { + var profiler = p.Split(':')[0]; + return $$$"""" void mono_profiler_init_{{{profiler}}} (const char *desc); EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_{{{profiler}}} (const char *desc) { mono_profiler_init_{{{profiler}}} (desc); } - """") + """";}) }} {{parsedAotMode switch From 35beefcef7e0c5e8b08f95480c3a547f2575a4f7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Feb 2025 00:12:40 +0100 Subject: [PATCH 10/25] more aot --- src/mono/mono/mini/mini-llvm.c | 47 +++++++++++- src/mono/mono/mini/mini-ops.h | 2 + src/mono/mono/mini/mini-profiler.c | 27 ------- src/mono/mono/mini/mini.c | 111 +++++++++++++++++++++-------- src/mono/mono/mini/mini.h | 1 - 5 files changed, 130 insertions(+), 58 deletions(-) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 2d02f942dd68b0..fe426fa2551e68 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -3749,6 +3749,41 @@ emit_gc_safepoint_poll (MonoLLVMModule *module, LLVMModuleRef lmodule, MonoCompi LLVMDisposeBuilder (builder); } +static void +emit_raise_method_samplepoint (MonoCompile *cfg, EmitContext *ctx, MonoBasicBlock *bb) +{ + printf ("emit_raise_method_samplepoint %s\n", cfg->llvm_method_name); + + g_assert (cfg->compile_aot); // other is not implemented + + LLVMBuilderRef builder; + LLVMValueRef callee, call, args [3]; + LLVMBasicBlockRef sample_bb; + static LLVMTypeRef sample_point_sig; + + // (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) + if (!sample_point_sig) + sample_point_sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), IntPtrType (), IntPtrType(), FALSE); + + sample_bb = gen_bb (ctx, "SAMPLE_BB"); + + // TODO + args [0] = LLVMConstNull (IntPtrType ()); + args [1] = LLVMConstNull (IntPtrType ()); + args [2] = LLVMConstNull (IntPtrType ()); + + ctx->builder = builder = create_builder (ctx); + + callee = get_callee (ctx, sample_point_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_profiler_raise_method_samplepoint)); + call = LLVMBuildCall2 (builder, sample_point_sig, callee, args, 3, ""); + set_call_cold_cconv (call); + LLVMBuildBr (builder, sample_bb); + + ctx->builder = builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (builder, sample_bb); + ctx->bblocks [bb->block_num].end_bblock = sample_bb; +} + static void emit_llvm_code_end (MonoLLVMModule *module) { @@ -7793,6 +7828,10 @@ MONO_RESTORE_WARNING break; } + case OP_GC_SAMPLE_POINT: { + emit_raise_method_samplepoint (cfg, ctx, bb); + break; + } case OP_GC_SAFE_POINT: { LLVMValueRef val, cmp, callee, call; LLVMBasicBlockRef poll_bb, cont_bb; @@ -13159,9 +13198,13 @@ emit_method_inner (EmitContext *ctx) LLVMSetGC (method, "coreclr"); } } - LLVMSetLinkage (method, LLVMPrivateLinkage); - // TODO emit samplepoint + if (requires_safepoint && MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) { + printf("TODO: %s \n", ctx->method_name); + //emit_raise_method_samplepoint (cfg, ctx, bb); + } + + LLVMSetLinkage (method, LLVMPrivateLinkage); mono_llvm_add_func_attr (method, LLVM_ATTR_UW_TABLE); diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 76a1c49c15e20e..a87684a5eeccbf 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1349,6 +1349,8 @@ MINI_OP(OP_GC_PARAM_SLOT_LIVENESS_DEF, "gc_param_slot_liveness_def", NONE, NONE, MINI_OP(OP_GC_SAFE_POINT, "gc_safe_point", NONE, IREG, NONE) +MINI_OP(OP_GC_SAMPLE_POINT, "prof_samplepoint", NONE, NONE, NONE) + /* * Check if the class given by sreg1 was inited, if not, call * mono_generic_class_init_trampoline () though a trampoline. diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index 28052a546db629..e1811e85360a8e 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -101,33 +101,6 @@ mini_profiler_emit_enter (MonoCompile *cfg) mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs); } -void -mini_profiler_emit_samplepoint (MonoCompile *cfg) -{ - gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - - if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) - return; - - if (cfg->current_method != cfg->method) - return; - - MonoInst *iargs [3]; - - EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); - EMIT_NEW_PCONST (cfg, iargs [1], NULL); - - if (MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) - iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); - else - EMIT_NEW_PCONST (cfg, iargs [2], NULL); - - /* void mono_profiler_raise_method_samplepoint (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ - if (trace) - mono_emit_jit_icall (cfg, mono_trace_samplepoint_method, iargs); - else - mono_emit_jit_icall (cfg, mono_profiler_raise_method_samplepoint, iargs); -} void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 4a6d83b84dc569..ceb9befc362e46 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2747,31 +2747,9 @@ insert_safepoint (MonoCompile *cfg, MonoBasicBlock *bblock) } } -/* -This code inserts safepoints into managed code at important code paths. -Those are: - --the first basic block --landing BB for exception handlers --loop body starts. - -*/ -static void -insert_safepoints (MonoCompile *cfg) +static bool +skip_insert_safepoint (MonoCompile *cfg) { - MonoBasicBlock *bb; - - g_assert (mini_safepoints_enabled ()); - - if (COMPILE_LLVM (cfg)) { - if (!cfg->llvm_only) { - /* We rely on LLVM's safepoints insertion capabilities. */ - if (cfg->verbose_level > 1) - printf ("SKIPPING SAFEPOINTS for code compiled with LLVM\n"); - return; - } - } - if (cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) { WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method); /* These wrappers are called from the wrapper for the polling function, leading to potential stack overflow */ @@ -2781,14 +2759,14 @@ insert_safepoints (MonoCompile *cfg) info->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced)) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for the polling function icall\n"); - return; + return true; } } if (cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for native-to-managed wrappers.\n"); - return; + return true; } if (cfg->method->wrapper_type == MONO_WRAPPER_OTHER) { @@ -2798,13 +2776,44 @@ insert_safepoints (MonoCompile *cfg) /* These wrappers shouldn't do any icalls */ if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for interp-in wrappers.\n"); - return; + return true; } } if (cfg->method->wrapper_type == MONO_WRAPPER_WRITE_BARRIER) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for write barrier wrappers.\n"); + return true; + } + return false; +} + +/* +This code inserts safepoints into managed code at important code paths. +Those are: + +-the first basic block +-landing BB for exception handlers +-loop body starts. + +*/ +static void +insert_safepoints (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + g_assert (mini_safepoints_enabled ()); + + if (COMPILE_LLVM (cfg)) { + if (!cfg->llvm_only) { + /* We rely on LLVM's safepoints insertion capabilities. */ + if (cfg->verbose_level > 1) + printf ("SKIPPING SAFEPOINTS for code compiled with LLVM\n"); + return; + } + } + + if (skip_insert_safepoint (cfg)) { return; } @@ -2834,6 +2843,52 @@ insert_safepoints (MonoCompile *cfg) } +static void +insert_samplepoint (MonoCompile *cfg, MonoBasicBlock *bblock) +{ + MonoInst *ins; + + if (cfg->verbose_level > 1) + printf ("ADDING MAPLE POINT TO BB %d\n", bblock->block_num); + + MONO_INST_NEW (cfg, ins, OP_GC_SAMPLE_POINT); + MONO_ADD_INS (bblock, ins); +} + +static void +insert_samplepoints (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + if (skip_insert_safepoint (cfg)) { + return; + } + + if (cfg->verbose_level > 1) + printf ("INSERTING SAMPLEPOINTS\n"); + if (cfg->verbose_level > 2) + mono_print_code (cfg, "BEFORE SAMPLEPOINTS"); + + /* if the method doesn't contain + * (1) a call (so it's a leaf method) + * (2) and no loops + * we can skip the samplepoint on method entry. */ + gboolean requires_samplepoint = cfg->has_calls; + + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + if (bb->loop_body_start || (bb->flags & BB_EXCEPTION_HANDLER)) { + requires_samplepoint = TRUE; + insert_samplepoint (cfg, bb); + } + } + + if (requires_samplepoint) + insert_samplepoint (cfg, cfg->bb_entry); + + if (cfg->verbose_level > 2) + mono_print_code (cfg, "AFTER SAMPLEPOINTS"); + +} static void mono_insert_branches_between_bblocks (MonoCompile *cfg) @@ -3678,7 +3733,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts mono_cfg_dump_ir (cfg, "insert_safepoints"); } - // TODO insert_samplepoints + insert_samplepoints (cfg); /* after method_to_ir */ if (parts == 1) { diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index a9ee123e32d7c8..97196e7e125cb4 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2124,7 +2124,6 @@ mono_bb_last_inst (MonoBasicBlock *bb, int filter) /* profiler support */ void mini_add_profiler_argument (const char *desc); -void mini_profiler_emit_samplepoint (MonoCompile *cfg); void mini_profiler_emit_enter (MonoCompile *cfg); void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret); void mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target); From ba8528b9ed8ef1555104573dd8338fb6383d7723 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Feb 2025 20:49:34 +0100 Subject: [PATCH 11/25] AOT --- src/mono/browser/runtime/profiler.ts | 8 ++-- src/mono/mono/mini/method-to-ir.c | 6 +-- src/mono/mono/mini/mini-llvm.c | 45 ------------------- src/mono/mono/mini/mini-ops.h | 2 - src/mono/mono/mini/mini-profiler.c | 26 ++++++++++- src/mono/mono/mini/mini.c | 28 +++++------- src/mono/mono/mini/mini.h | 2 + .../browser-advanced/ILLink.Descriptors.xml | 8 ---- .../sample/wasm/browser-advanced/Program.cs | 30 +++++++++++++ .../Wasm.Advanced.Sample.csproj | 6 --- src/mono/sample/wasm/browser-advanced/main.js | 2 + 11 files changed, 76 insertions(+), 87 deletions(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 879cee96d8905f..fe5e3d6e63ec94 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -102,7 +102,7 @@ export function mono_wasm_profiler_enter (): void { } stackFrames.push(globalThis.performance.now()); - stackFramesSkip.push(skipSample()); + stackFramesSkip.push(skip_sample()); } const methodNames: Map = new Map(); @@ -115,7 +115,7 @@ export function mono_wasm_profiler_leave (method: MonoMethod): void { const skip = stackFramesSkip.pop(); // no sample point and skip also now - if (skip && skipSample()) { + if (skip && skip_sample()) { return; } @@ -138,7 +138,7 @@ export function mono_wasm_profiler_samplepoint (): void { if (!runtimeHelpers.enablePerfMeasure) { return; } - if (!stackFramesSkip[stackFramesSkip.length - 1] || skipSample()) { + if (!stackFramesSkip[stackFramesSkip.length - 1] || skip_sample()) { return; } @@ -146,7 +146,7 @@ export function mono_wasm_profiler_samplepoint (): void { stackFramesSkip[stackFramesSkip.length - 1] = false; } -function skipSample ():boolean { +function skip_sample ():boolean { sampleSkipCounter++; if (sampleSkipCounter < skipsPerPeriod) { return true; diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 1f919ebde5fa5b..d966d0ec0de6bf 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -8691,9 +8691,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; } case MONO_CEE_RET: - if (!detached_before_ret) { + if (!detached_before_ret) mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL); - } g_assert (!method_does_not_return (method)); @@ -11485,9 +11484,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (sp != stack_start) UNVERIFIED; - if (!detached_before_ret) { + if (!detached_before_ret) mini_profiler_emit_leave (cfg, sp [0]); - } MONO_INST_NEW (cfg, ins, OP_BR); ins->inst_target_bb = end_bblock; diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index fe426fa2551e68..8cc8c986381b52 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -3749,41 +3749,6 @@ emit_gc_safepoint_poll (MonoLLVMModule *module, LLVMModuleRef lmodule, MonoCompi LLVMDisposeBuilder (builder); } -static void -emit_raise_method_samplepoint (MonoCompile *cfg, EmitContext *ctx, MonoBasicBlock *bb) -{ - printf ("emit_raise_method_samplepoint %s\n", cfg->llvm_method_name); - - g_assert (cfg->compile_aot); // other is not implemented - - LLVMBuilderRef builder; - LLVMValueRef callee, call, args [3]; - LLVMBasicBlockRef sample_bb; - static LLVMTypeRef sample_point_sig; - - // (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) - if (!sample_point_sig) - sample_point_sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), IntPtrType (), IntPtrType(), FALSE); - - sample_bb = gen_bb (ctx, "SAMPLE_BB"); - - // TODO - args [0] = LLVMConstNull (IntPtrType ()); - args [1] = LLVMConstNull (IntPtrType ()); - args [2] = LLVMConstNull (IntPtrType ()); - - ctx->builder = builder = create_builder (ctx); - - callee = get_callee (ctx, sample_point_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_profiler_raise_method_samplepoint)); - call = LLVMBuildCall2 (builder, sample_point_sig, callee, args, 3, ""); - set_call_cold_cconv (call); - LLVMBuildBr (builder, sample_bb); - - ctx->builder = builder = create_builder (ctx); - LLVMPositionBuilderAtEnd (builder, sample_bb); - ctx->bblocks [bb->block_num].end_bblock = sample_bb; -} - static void emit_llvm_code_end (MonoLLVMModule *module) { @@ -7828,10 +7793,6 @@ MONO_RESTORE_WARNING break; } - case OP_GC_SAMPLE_POINT: { - emit_raise_method_samplepoint (cfg, ctx, bb); - break; - } case OP_GC_SAFE_POINT: { LLVMValueRef val, cmp, callee, call; LLVMBasicBlockRef poll_bb, cont_bb; @@ -13198,12 +13159,6 @@ emit_method_inner (EmitContext *ctx) LLVMSetGC (method, "coreclr"); } } - - if (requires_safepoint && MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) { - printf("TODO: %s \n", ctx->method_name); - //emit_raise_method_samplepoint (cfg, ctx, bb); - } - LLVMSetLinkage (method, LLVMPrivateLinkage); mono_llvm_add_func_attr (method, LLVM_ATTR_UW_TABLE); diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index a87684a5eeccbf..76a1c49c15e20e 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1349,8 +1349,6 @@ MINI_OP(OP_GC_PARAM_SLOT_LIVENESS_DEF, "gc_param_slot_liveness_def", NONE, NONE, MINI_OP(OP_GC_SAFE_POINT, "gc_safe_point", NONE, IREG, NONE) -MINI_OP(OP_GC_SAMPLE_POINT, "prof_samplepoint", NONE, NONE, NONE) - /* * Check if the class given by sreg1 was inited, if not, call * mono_generic_class_init_trampoline () though a trampoline. diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index e1811e85360a8e..a732663a6056b3 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -73,6 +73,30 @@ can_encode_method_ref (MonoMethod *method) return (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD); } +void +mini_profiler_emit_samplepoint (MonoCompile *cfg) +{ + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); + + if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + return; + + if (cfg->current_method != cfg->method) + return; + + MonoInst *iargs [3]; + + EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); + EMIT_NEW_PCONST (cfg, iargs [1], NULL); + iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); + + /* void mono_profiler_raise_method_samplepoint (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ + if (trace) + mono_emit_jit_icall (cfg, mono_trace_samplepoint_method, iargs); + else + mono_emit_jit_icall (cfg, mono_profiler_raise_method_samplepoint, iargs); +} + void mini_profiler_emit_enter (MonoCompile *cfg) { @@ -101,7 +125,6 @@ mini_profiler_emit_enter (MonoCompile *cfg) mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs); } - void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) { @@ -373,4 +396,3 @@ mini_profiler_context_free_buffer (void *buffer) { g_free (buffer); } - diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index ceb9befc362e46..357306ab2dff21 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2846,13 +2846,13 @@ insert_safepoints (MonoCompile *cfg) static void insert_samplepoint (MonoCompile *cfg, MonoBasicBlock *bblock) { - MonoInst *ins; - if (cfg->verbose_level > 1) - printf ("ADDING MAPLE POINT TO BB %d\n", bblock->block_num); + printf ("ADDING SAMPLE POINT TO BB %d\n", bblock->block_num); - MONO_INST_NEW (cfg, ins, OP_GC_SAMPLE_POINT); - MONO_ADD_INS (bblock, ins); + MonoBasicBlock *prev_cbb = cfg->cbb; + cfg->cbb = bblock; + mini_profiler_emit_samplepoint (cfg); + cfg->cbb = prev_cbb; } static void @@ -2869,21 +2869,13 @@ insert_samplepoints (MonoCompile *cfg) if (cfg->verbose_level > 2) mono_print_code (cfg, "BEFORE SAMPLEPOINTS"); - /* if the method doesn't contain - * (1) a call (so it's a leaf method) - * (2) and no loops - * we can skip the samplepoint on method entry. */ - gboolean requires_samplepoint = cfg->has_calls; - for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { if (bb->loop_body_start || (bb->flags & BB_EXCEPTION_HANDLER)) { - requires_samplepoint = TRUE; insert_samplepoint (cfg, bb); } } - if (requires_samplepoint) - insert_samplepoint (cfg, cfg->bb_entry); + // we don't need samplepoint event on method entry, there is already a method entry event if (cfg->verbose_level > 2) mono_print_code (cfg, "AFTER SAMPLEPOINTS"); @@ -3732,8 +3724,11 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts MONO_TIME_TRACK (mono_jit_stats.jit_insert_safepoints, insert_safepoints (cfg)); mono_cfg_dump_ir (cfg, "insert_safepoints"); } - - insert_samplepoints (cfg); + + if (MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) { + MONO_TIME_TRACK (mono_jit_stats.jit_insert_samplepoints, insert_samplepoints (cfg)); + mono_cfg_dump_ir (cfg, "insert_samplepoints"); + } /* after method_to_ir */ if (parts == 1) { @@ -4430,6 +4425,7 @@ mini_jit_init (void) mono_counters_register ("JIT/compile_dominator_info", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_compile_dominator_info); mono_counters_register ("JIT/compute_natural_loops", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_compute_natural_loops); mono_counters_register ("JIT/insert_safepoints", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_insert_safepoints); + mono_counters_register ("JIT/insert_samplepoints", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_insert_samplepoints); mono_counters_register ("JIT/ssa_compute", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_ssa_compute); mono_counters_register ("JIT/ssa_cprop", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_ssa_cprop); mono_counters_register ("JIT/ssa_deadce", MONO_COUNTER_JIT | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_jit_stats.jit_ssa_deadce); diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 97196e7e125cb4..26ebd7f90fdf6b 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1740,6 +1740,7 @@ typedef struct { gint64 jit_compile_dominator_info; gint64 jit_compute_natural_loops; gint64 jit_insert_safepoints; + gint64 jit_insert_samplepoints; gint64 jit_ssa_compute; gint64 jit_ssa_cprop; gint64 jit_ssa_deadce; @@ -2125,6 +2126,7 @@ mono_bb_last_inst (MonoBasicBlock *bb, int filter) /* profiler support */ void mini_add_profiler_argument (const char *desc); void mini_profiler_emit_enter (MonoCompile *cfg); +void mini_profiler_emit_samplepoint (MonoCompile *cfg); void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret); void mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target); void mini_profiler_emit_call_finally (MonoCompile *cfg, MonoMethodHeader *header, unsigned char *ip, guint32 index, MonoExceptionClause *clause); diff --git a/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml b/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml index f418cc51be4b32..665eae765f7332 100644 --- a/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml +++ b/src/mono/sample/wasm/browser-advanced/ILLink.Descriptors.xml @@ -5,12 +5,4 @@ - - - - - - - - diff --git a/src/mono/sample/wasm/browser-advanced/Program.cs b/src/mono/sample/wasm/browser-advanced/Program.cs index 093d8c40404194..a46f23c267953f 100644 --- a/src/mono/sample/wasm/browser-advanced/Program.cs +++ b/src/mono/sample/wasm/browser-advanced/Program.cs @@ -66,6 +66,36 @@ internal static int TestMeaning() return Add(half, half); } + [JSExport] + internal static void SillyLoop() + { + // this silly method will generate few sample points for the profiler + for (int i = 1; i <= 60; i ++) + { + try + { + for (int s = 0; s <= 60; s ++) + { + try + { + if (DateTime.UtcNow.Millisecond == s) + { + Console.WriteLine("Time is " + s); + } + } + catch(Exception e) + { + Console.WriteLine(e.Message); + } + } + } + catch(Exception e) + { + Console.WriteLine(e.Message); + } + } + } + [JSExport] internal static bool IsPrime(int number) { diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index 5e9f768bd3ed12..66917272800573 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -18,12 +18,6 @@ ./ - - - true - false diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index 4e8dd182f6109c..706cfcce36f156 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -108,6 +108,8 @@ try { const deepMeaning = new Promise(resolve => setTimeout(() => resolve(meaning), 100)); exports.Sample.Test.PrintMeaning(deepMeaning); + exports.Sample.Test.SillyLoop(); + let exit_code = await runMain(config.mainAssemblyName, []); exit(exit_code); } From 388647b1c602e9a019f6ee3161d9e2d517cedd31 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Feb 2025 19:51:32 +0100 Subject: [PATCH 12/25] jiterp, sampling in C, INTERP_PROFILER_RAISE macro --- src/mono/browser/runtime/cwraps.ts | 3 + src/mono/browser/runtime/exports-binding.ts | 7 +- .../browser/runtime/jiterpreter-support.ts | 21 ++ .../runtime/jiterpreter-trace-generator.ts | 9 +- src/mono/browser/runtime/jiterpreter.ts | 30 +++ src/mono/browser/runtime/profiler.ts | 77 +------- src/mono/mono/metadata/jit-icall-reg.h | 2 +- src/mono/mono/mini/interp/interp.c | 104 +++++----- src/mono/mono/mini/interp/jiterpreter.h | 9 + src/mono/mono/mini/interp/mintops.def | 2 +- src/mono/mono/mini/mini-profiler.c | 32 +-- src/mono/mono/mini/mini-runtime.c | 2 +- src/mono/mono/mini/trace.c | 8 +- src/mono/mono/profiler/browser.c | 186 +++++++++++++++--- .../Wasm.Advanced.Sample.csproj | 6 + src/mono/sample/wasm/browser-advanced/main.js | 4 +- .../public/mono/metadata/profiler-events.h | 2 +- 17 files changed, 320 insertions(+), 184 deletions(-) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index f7a7c367e7139a..51c600a2e35c75 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -129,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, ]; diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 7ae33aed064486..050c67cdc65c63 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -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, mono_wasm_profiler_samplepoint } 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"; @@ -70,9 +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_samplepoint, + mono_wasm_profiler_now, + mono_wasm_profiler_record, // driver.c mono_wasm_trace_logger, diff --git a/src/mono/browser/runtime/jiterpreter-support.ts b/src/mono/browser/runtime/jiterpreter-support.ts index e914669dbb444e..5bd39ede34563b 100644 --- a/src/mono/browser/runtime/jiterpreter-support.ts +++ b/src/mono/browser/runtime/jiterpreter-support.ts @@ -1600,6 +1600,27 @@ export const _now = (globalThis.performance && globalThis.performance.now) let scratchBuffer: NativePointer = 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) diff --git a/src/mono/browser/runtime/jiterpreter-trace-generator.ts b/src/mono/browser/runtime/jiterpreter-trace-generator.ts index fb36b03e2cdfe6..b777f01e285ba9 100644 --- a/src/mono/browser/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/browser/runtime/jiterpreter-trace-generator.ts @@ -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, @@ -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, diff --git a/src/mono/browser/runtime/jiterpreter.ts b/src/mono/browser/runtime/jiterpreter.ts index abaa7d63cd8e56..fa15904fa46492 100644 --- a/src/mono/browser/runtime/jiterpreter.ts +++ b/src/mono/browser/runtime/jiterpreter.ts @@ -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]; @@ -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", { diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index fe5e3d6e63ec94..9c3e3a77b3cf5d 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -30,13 +30,13 @@ export function mono_wasm_init_browser_profiler (options: BrowserProfilerOptions mono_assert(runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "Browser profiler is not enabled, please use browser; in your project file."); if (options == null) options = {}; - if (typeof options.sampleIntervalMs === "number") { - desiredSampleIntervalMs = options.sampleIntervalMs!; - } 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); } @@ -88,40 +88,12 @@ export function endMeasure (start: TimeStamp, block: string, id?: string) { } } -let desiredSampleIntervalMs = 10;// ms -let lastSampleTime = 0; -let prevskipsPerPeriod = 1; -let skipsPerPeriod = 1; -let sampleSkipCounter = 0; -const stackFrames: number[] = []; -const stackFramesSkip: boolean[] = []; - -export function mono_wasm_profiler_enter (): void { - if (!runtimeHelpers.enablePerfMeasure) { - return; - } - - stackFrames.push(globalThis.performance.now()); - stackFramesSkip.push(skip_sample()); +export function mono_wasm_profiler_now (): number { + return globalThis.performance.now(); } const methodNames: Map = new Map(); -export function mono_wasm_profiler_leave (method: MonoMethod): void { - if (!runtimeHelpers.enablePerfMeasure) { - return; - } - - const start = stackFrames.pop(); - const skip = stackFramesSkip.pop(); - - // no sample point and skip also now - if (skip && skip_sample()) { - return; - } - - // propagate !skip to parent - stackFramesSkip[stackFramesSkip.length - 1] = false; - +export function mono_wasm_profiler_record (method: MonoMethod, start: number): void { const options = ENVIRONMENT_IS_WEB ? { start: start } : { startTime: start }; @@ -133,40 +105,3 @@ export function mono_wasm_profiler_leave (method: MonoMethod): void { } globalThis.performance.measure(methodName, options); } - -export function mono_wasm_profiler_samplepoint (): void { - if (!runtimeHelpers.enablePerfMeasure) { - return; - } - if (!stackFramesSkip[stackFramesSkip.length - 1] || skip_sample()) { - return; - } - - // mark the current frame to not skip - stackFramesSkip[stackFramesSkip.length - 1] = false; -} - -function skip_sample ():boolean { - sampleSkipCounter++; - if (sampleSkipCounter < skipsPerPeriod) { - return true; - } - - // timer resolution in non-isolated contexts: 100 microseconds (decimal number) - const now = globalThis.performance.now(); - - if (desiredSampleIntervalMs > 0 && lastSampleTime != 0) { - // recalculate ideal number of skips per period - const msSinceLastSample = now - lastSampleTime; - const skipsPerMs = sampleSkipCounter / msSinceLastSample; - const newSkipsPerPeriod = (skipsPerMs * desiredSampleIntervalMs); - skipsPerPeriod = ((newSkipsPerPeriod + sampleSkipCounter + prevskipsPerPeriod) / 3) | 0; - prevskipsPerPeriod = sampleSkipCounter; - } else { - skipsPerPeriod = 0; - } - lastSampleTime = now; - sampleSkipCounter = 0; - - return false; -} diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index 368f6682228197..c844391d31cea9 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -249,8 +249,8 @@ MONO_JIT_ICALL (mono_object_isinst_with_cache) \ 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_samplepoint) \ 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) \ diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 05f53abac492d4..aa1f71603e2a45 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -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 @@ -7607,47 +7625,22 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 3; MINT_IN_BREAK; } - MINT_IN_CASE(MINT_PROF_SAMPLEPOINT) { + MINT_IN_CASE(MINT_PROF_ENTER) { guint16 flag = ip [1]; ip += 2; - - if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_samplepoint) && - (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_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_samplepoint_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); - if (flag & PROFILING_FLAG) - MONO_PROFILER_RAISE (method_samplepoint, (frame->imethod->method, prof_ctx)); - g_free (prof_ctx); - } else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_samplepoint)) { - MONO_PROFILER_RAISE (method_samplepoint, (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_ENTER) { + MINT_IN_CASE(MINT_PROF_SAMPLEPOINT) { 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(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; @@ -7660,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_leave)) { - 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; } @@ -9146,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; @@ -9182,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) { diff --git a/src/mono/mono/mini/interp/jiterpreter.h b/src/mono/mono/mini/interp/jiterpreter.h index e9b3c67cb29374..9030da2c06a28e 100644 --- a/src/mono/mono/mini/interp/jiterpreter.h +++ b/src/mono/mono/mini/interp/jiterpreter.h @@ -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); diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 0b7d0125a88ece..d8742cf4ecb7be 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -793,8 +793,8 @@ OPDEF(MINT_ABSF, "abs_f", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_MINF, "min_f", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_MAXF, "max_f", 4, 1, 2, MintOpNoArgs) -OPDEF(MINT_PROF_SAMPLEPOINT, "prof_samplepoint", 2, 0, 0, MintOpShortInt) 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) diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index a732663a6056b3..d646afcbfc7939 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -74,11 +74,11 @@ can_encode_method_ref (MonoMethod *method) } void -mini_profiler_emit_samplepoint (MonoCompile *cfg) +mini_profiler_emit_enter (MonoCompile *cfg) { gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + if ((!MONO_CFG_PROFILE (cfg, ENTER) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) return; if (cfg->current_method != cfg->method) @@ -88,21 +88,25 @@ mini_profiler_emit_samplepoint (MonoCompile *cfg) EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); EMIT_NEW_PCONST (cfg, iargs [1], NULL); - iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); - /* void mono_profiler_raise_method_samplepoint (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ + if (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT)) + iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); + else + EMIT_NEW_PCONST (cfg, iargs [2], NULL); + + /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ if (trace) - mono_emit_jit_icall (cfg, mono_trace_samplepoint_method, iargs); + mono_emit_jit_icall (cfg, mono_trace_enter_method, iargs); else - mono_emit_jit_icall (cfg, mono_profiler_raise_method_samplepoint, iargs); + mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs); } void -mini_profiler_emit_enter (MonoCompile *cfg) +mini_profiler_emit_samplepoint (MonoCompile *cfg) { gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if ((!MONO_CFG_PROFILE (cfg, ENTER) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) return; if (cfg->current_method != cfg->method) @@ -112,17 +116,13 @@ mini_profiler_emit_enter (MonoCompile *cfg) EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); EMIT_NEW_PCONST (cfg, iargs [1], NULL); + iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); - if (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT)) - iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL); - else - EMIT_NEW_PCONST (cfg, iargs [2], NULL); - - /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ + /* void mono_profiler_raise_method_samplepoint (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */ if (trace) - mono_emit_jit_icall (cfg, mono_trace_enter_method, iargs); + mono_emit_jit_icall (cfg, mono_trace_samplepoint_method, iargs); else - mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs); + mono_emit_jit_icall (cfg, mono_profiler_raise_method_samplepoint, iargs); } void diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 92e6cfa979e88a..eb2ff7d4df81ae 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4939,8 +4939,8 @@ register_icalls (void) * the wrapper would call the icall which would call the wrapper and * so on. */ - register_icall (mono_profiler_raise_method_samplepoint, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_enter, mono_icall_sig_void_ptr_ptr, TRUE); + register_icall (mono_profiler_raise_method_samplepoint, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_leave, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_method_tail_call, mono_icall_sig_void_ptr_ptr, TRUE); register_icall (mono_profiler_raise_exception_clause, mono_icall_sig_void_ptr_int_int_object, TRUE); diff --git a/src/mono/mono/mini/trace.c b/src/mono/mono/mini/trace.c index bec8f13fa45421..fd82208184be9d 100644 --- a/src/mono/mono/mini/trace.c +++ b/src/mono/mono/mini/trace.c @@ -142,15 +142,15 @@ frame_kind (MonoJitInfo *ji) static void mono_trace_enter_method_impl (const char *prefix, MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx); void -mono_trace_samplepoint_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) +mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) { - mono_trace_enter_method_impl ("SAMPLEPOINT:%c %s(", method, ji, ctx); + mono_trace_enter_method_impl ("ENTER:%c %s(", method, ji, ctx); } void -mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) +mono_trace_samplepoint_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) { - mono_trace_enter_method_impl ("ENTER:%c %s(", method, ji, ctx); + mono_trace_enter_method_impl ("SAMPLEPOINT:%c %s(", method, ji, ctx); } static void diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index 6c20c98b3312a8..88a3dd29aa6f52 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -30,36 +30,97 @@ struct _MonoProfiler { }; static MonoProfiler browser_profiler; +static int desired_sample_interval_ms = 10;// ms static MonoCallSpec callspec; - #ifdef HOST_WASM -void -mono_wasm_profiler_enter (); +typedef struct _ProfilerStackFrame ProfilerStackFrame; -void -mono_wasm_profiler_leave (MonoMethod *method); +struct _ProfilerStackFrame { + double start; + bool should_record; + ProfilerStackFrame *next; +}; -void -mono_wasm_profiler_samplepoint (); +static ProfilerStackFrame *profiler_stack = NULL; +static double last_sample_time = 0; +static int prev_skips_per_period = 1; +static int skips_per_period = 1; +static int sample_skip_counter = 1; + +double mono_wasm_profiler_now (); +void mono_wasm_profiler_record (MonoMethod *method, double start); + +static bool should_record_frame (double now) +{ + if (sample_skip_counter < skips_per_period) { + return FALSE; + } + + // timer resolution in non-isolated contexts: 100 microseconds (decimal number) + double ms_since_last_sample = now - last_sample_time; + + if (desired_sample_interval_ms > 0 && last_sample_time != 0) { + // recalculate ideal number of skips per period + double skips_per_ms = ((double)sample_skip_counter) / ms_since_last_sample; + double newskips_per_period = (skips_per_ms * ((double)desired_sample_interval_ms)); + skips_per_period = ((newskips_per_period + ((double)sample_skip_counter) + ((double)prev_skips_per_period)) / 3); + prev_skips_per_period = sample_skip_counter; + } else { + skips_per_period = 0; + } + last_sample_time = now; + sample_skip_counter = 0; + + return TRUE; +} static void method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { - mono_wasm_profiler_enter (); + sample_skip_counter++; + + ProfilerStackFrame* frame = g_new0 (ProfilerStackFrame, 1); + double now = frame->start = mono_wasm_profiler_now (); + frame->should_record = should_record_frame (now); + frame->next = profiler_stack; + profiler_stack = frame; } static void method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { - mono_wasm_profiler_samplepoint (); + g_assert(profiler_stack); + + sample_skip_counter++; + + if (!profiler_stack->should_record) + { + profiler_stack->should_record = should_record_frame (mono_wasm_profiler_now ()); + } } static void method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { - mono_wasm_profiler_leave (method); + g_assert(profiler_stack); + + sample_skip_counter++; + + // pop top frame + ProfilerStackFrame *top = profiler_stack; + profiler_stack = profiler_stack->next; + + if (top->should_record || should_record_frame (mono_wasm_profiler_now ())) + { + // propagate keep to parent, if any + if(profiler_stack) + profiler_stack->should_record = TRUE; + + mono_wasm_profiler_record (method, top->start); + } + g_free (top); } static void @@ -114,6 +175,90 @@ match_option (const char *arg, const char *opt_name, const char **rval) } } +static void +parse_arg (const char *arg) +{ + const char *val; + + if (match_option (arg, "callspec", &val)) { + if (!val) + val = ""; + if (val[0] == '\"') + ++val; + char *spec = g_strdup (val); + size_t speclen = strlen (val); + if (speclen > 0 && spec[speclen - 1] == '\"') + spec[speclen - 1] = '\0'; + char *errstr; + if (!mono_callspec_parse (spec, &callspec, &errstr)) { + mono_profiler_printf_err ("Could not parse callspec '%s': %s", spec, errstr); + g_free (errstr); + mono_callspec_cleanup (&callspec); + } + g_free (spec); + } + else if (match_option (arg, "interval", &val)) { + char *end; + desired_sample_interval_ms = strtoul (val, &end, 10); + } +} + +static void +proflog_parse_args (const char *desc) +{ + const char *p; + gboolean in_quotes = FALSE; + char quote_char = '\0'; + char *buffer = g_malloc (strlen (desc) + 1); + int buffer_pos = 0; + + for (p = desc; *p; p++){ + switch (*p){ + case ',': + if (!in_quotes) { + if (buffer_pos != 0){ + buffer [buffer_pos] = 0; + parse_arg (buffer); + buffer_pos = 0; + } + } else { + buffer [buffer_pos++] = *p; + } + break; + + case '\\': + if (p [1]) { + buffer [buffer_pos++] = p[1]; + p++; + } + break; + case '\'': + case '"': + if (in_quotes) { + if (quote_char == *p) + in_quotes = FALSE; + else + buffer [buffer_pos++] = *p; + } else { + in_quotes = TRUE; + quote_char = *p; + } + break; + default: + buffer [buffer_pos++] = *p; + break; + } + } + + if (buffer_pos != 0) { + buffer [buffer_pos] = 0; + parse_arg (buffer); + } + + g_free (buffer); +} + + /** * mono_profiler_init_browser: * the entry point @@ -123,26 +268,7 @@ mono_profiler_init_browser (const char *desc) { // browser: if (desc && desc [7] == ':') { - const char *arg = desc + 8; - const char *val; - - if (match_option (arg, "callspec", &val)) { - if (!val) - val = ""; - if (val[0] == '\"') - ++val; - char *spec = g_strdup (val); - size_t speclen = strlen (val); - if (speclen > 0 && spec[speclen - 1] == '\"') - spec[speclen - 1] = '\0'; - char *errstr; - if (!mono_callspec_parse (spec, &callspec, &errstr)) { - mono_profiler_printf_err ("Could not parse callspec '%s': %s", spec, errstr); - g_free (errstr); - mono_callspec_cleanup (&callspec); - } - g_free (spec); - } + proflog_parse_args (desc + 8); } MonoProfilerHandle handle = mono_profiler_create (&browser_profiler); diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index 66917272800573..88824cf2b9ec81 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -18,6 +18,12 @@ ./ + + diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index 706cfcce36f156..9f57c7c77c3b77 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -53,8 +53,8 @@ try { // config is loaded and could be tweaked before the rest of the runtime startup sequence config.environmentVariables["MONO_LOG_LEVEL"] = "debug"; config.browserProfilerOptions = { - sampleIntervalMs: 1, - callSpec: "N:Sample" + sampleIntervalMs: 5, + // callSpec: "N:Sample" }; }, preInit: () => { console.log('user code Module.preInit'); }, diff --git a/src/native/public/mono/metadata/profiler-events.h b/src/native/public/mono/metadata/profiler-events.h index 2ae8c3f15c6ea2..15363a23c6332d 100644 --- a/src/native/public/mono/metadata/profiler-events.h +++ b/src/native/public/mono/metadata/profiler-events.h @@ -71,13 +71,13 @@ MONO_PROFILER_EVENT_1(assembly_unloading, AssemblyLUnloading, MonoAssembly *, as MONO_PROFILER_EVENT_1(assembly_unloaded, AssemblyLUnloaded, MonoAssembly *, assembly) MONO_PROFILER_EVENT_2(method_enter, MethodEnter, MonoMethod *, method, MonoProfilerCallContext *, context) +MONO_PROFILER_EVENT_2(method_samplepoint, MethodSamplepoint, MonoMethod *, method, MonoProfilerCallContext *, context) MONO_PROFILER_EVENT_2(method_leave, MethodLeave, MonoMethod *, method, MonoProfilerCallContext *, context) MONO_PROFILER_EVENT_2(method_tail_call, MethodTailCall, MonoMethod *, method, MonoMethod *, target) MONO_PROFILER_EVENT_2(method_exception_leave, MethodExceptionLeave, MonoMethod *, method, MonoObject *, exception) MONO_PROFILER_EVENT_1(method_free, MethodFree, MonoMethod *, method) MONO_PROFILER_EVENT_1(method_begin_invoke, MethodBeginInvoke, MonoMethod *, method) MONO_PROFILER_EVENT_1(method_end_invoke, MethodEndInvoke, MonoMethod *, method) -MONO_PROFILER_EVENT_2(method_samplepoint, MethodSamplepoint, MonoMethod *, method, MonoProfilerCallContext *, context) MONO_PROFILER_EVENT_1(exception_throw, ExceptionThrow, MonoObject *, exception) MONO_PROFILER_EVENT_4(exception_clause, ExceptionClause, MonoMethod *, method, uint32_t, index, MonoExceptionEnum, type, MonoObject *, exception) From fe2ac7e77f89cabd6df02f765b68df0876810ca4 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Feb 2025 20:01:46 +0100 Subject: [PATCH 13/25] cleanup --- src/mono/mono/profiler/browser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index 88a3dd29aa6f52..a43c2380cbafed 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -204,7 +204,7 @@ parse_arg (const char *arg) } static void -proflog_parse_args (const char *desc) +parse_args (const char *desc) { const char *p; gboolean in_quotes = FALSE; @@ -268,7 +268,7 @@ mono_profiler_init_browser (const char *desc) { // browser: if (desc && desc [7] == ':') { - proflog_parse_args (desc + 8); + parse_args (desc + 8); } MonoProfilerHandle handle = mono_profiler_create (&browser_profiler); From 147ff0e9d5a1528f4e318c1de1475a42dfdf3b93 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 17 Feb 2025 14:54:22 +0100 Subject: [PATCH 14/25] cleanup --- src/mono/browser/runtime/profiler.ts | 4 +- src/mono/browser/runtime/types/internal.ts | 6 ++- src/mono/mono/mini/mini-profiler.c | 26 +++++++----- src/mono/mono/profiler/browser.c | 40 ++++++++++++++----- src/mono/sample/wasm/browser-advanced/main.js | 2 +- 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 9c3e3a77b3cf5d..5dbbc3d824160a 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -32,10 +32,10 @@ export function mono_wasm_init_browser_profiler (options: BrowserProfilerOptions options = {}; let arg = "browser:"; if (typeof options.callSpec === "string") { - arg += `callspec=${options.callSpec}`; + arg += `callspec=${options.callSpec},`; } if (typeof options.sampleIntervalMs === "number") { - arg += `interval=${options.sampleIntervalMs}`; + arg += `interval=${options.sampleIntervalMs},`; } cwraps.mono_wasm_profiler_init_browser(arg); } diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 7aa97a5f5c10f5..af942fc91bc2dc 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -260,7 +260,11 @@ 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 + /** + * See callspec in https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md#trace-monovm-profiler-events-during-startup + * When used together with Mono AOT, the callspec needs to match one in browser:callspec=N:Sample; in your project file. + */ + callSpec?: number, } export type LogProfilerOptions = { diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index d646afcbfc7939..b1164b8b9d2f76 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -76,12 +76,12 @@ can_encode_method_ref (MonoMethod *method) void mini_profiler_emit_enter (MonoCompile *cfg) { - gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - - if ((!MONO_CFG_PROFILE (cfg, ENTER) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + if (cfg->current_method != cfg->method) return; - if (cfg->current_method != cfg->method) + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); + + if (!trace && (!MONO_CFG_PROFILE (cfg, ENTER) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; @@ -104,12 +104,12 @@ mini_profiler_emit_enter (MonoCompile *cfg) void mini_profiler_emit_samplepoint (MonoCompile *cfg) { - gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - - if ((!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) && !trace) + if (cfg->current_method != cfg->method) return; - if (cfg->current_method != cfg->method) + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); + + if (!trace && (!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; @@ -128,9 +128,12 @@ mini_profiler_emit_samplepoint (MonoCompile *cfg) void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) { + if (cfg->current_method != cfg->method) + return; + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if (!MONO_CFG_PROFILE (cfg, LEAVE) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method))) + if (!trace && (!MONO_CFG_PROFILE (cfg, LEAVE) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; @@ -153,9 +156,12 @@ mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) void mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target) { + if (cfg->current_method != cfg->method) + return; + gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if ((!MONO_CFG_PROFILE (cfg, TAIL_CALL) || cfg->current_method != cfg->method) && !trace) + if (!trace && (!MONO_CFG_PROFILE (cfg, TAIL_CALL) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; g_assert (cfg->current_method == cfg->method); diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index a43c2380cbafed..bffca70d214d5f 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -30,24 +30,26 @@ struct _MonoProfiler { }; static MonoProfiler browser_profiler; -static int desired_sample_interval_ms = 10;// ms +static int desired_sample_interval_ms; static MonoCallSpec callspec; -#ifdef HOST_WASM +#ifdef HOST_BROWSER typedef struct _ProfilerStackFrame ProfilerStackFrame; struct _ProfilerStackFrame { + MonoMethod *method; double start; bool should_record; ProfilerStackFrame *next; }; -static ProfilerStackFrame *profiler_stack = NULL; -static double last_sample_time = 0; -static int prev_skips_per_period = 1; -static int skips_per_period = 1; -static int sample_skip_counter = 1; +static ProfilerStackFrame *profiler_stack; +static double last_sample_time; +static int prev_skips_per_period; +static int skips_per_period; +static int sample_skip_counter; +static int stack_depth; double mono_wasm_profiler_now (); void mono_wasm_profiler_record (MonoMethod *method, double start); @@ -80,17 +82,20 @@ static void method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { sample_skip_counter++; + stack_depth++; ProfilerStackFrame* frame = g_new0 (ProfilerStackFrame, 1); double now = frame->start = mono_wasm_profiler_now (); frame->should_record = should_record_frame (now); frame->next = profiler_stack; + frame->method = method; profiler_stack = frame; } static void method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter g_assert(profiler_stack); sample_skip_counter++; @@ -104,13 +109,18 @@ method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallCont static void method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter g_assert(profiler_stack); sample_skip_counter++; + stack_depth--; // pop top frame ProfilerStackFrame *top = profiler_stack; profiler_stack = profiler_stack->next; + + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter + g_assert(top->method == method); if (top->should_record || should_record_frame (mono_wasm_profiler_now ())) { @@ -135,7 +145,7 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) method_leave (prof, method, NULL); } -#endif /* HOST_WASM */ +#endif /* HOST_BROWSER */ static MonoProfilerCallInstrumentationFlags method_filter (MonoProfiler *prof, MonoMethod *method) @@ -266,6 +276,9 @@ parse_args (const char *desc) void mono_profiler_init_browser (const char *desc) { + desired_sample_interval_ms = 10;// ms + memset (&callspec, 0, sizeof (MonoCallSpec)); + // browser: if (desc && desc [7] == ':') { parse_args (desc + 8); @@ -279,12 +292,19 @@ mono_profiler_init_browser (const char *desc) return; } -#ifdef HOST_WASM +#ifdef HOST_BROWSER + profiler_stack = NULL; + last_sample_time = 0; + prev_skips_per_period = 1; + skips_per_period = 1; + sample_skip_counter = 1; + stack_depth = 0; + // install this only in production run, not in AOT run mono_profiler_set_method_samplepoint_callback (handle, method_samplepoint); mono_profiler_set_method_enter_callback (handle, method_enter); mono_profiler_set_method_leave_callback (handle, method_leave); mono_profiler_set_method_tail_call_callback (handle, tail_call); mono_profiler_set_method_exception_leave_callback (handle, method_exc_leave); -#endif /* HOST_WASM */ +#endif /* HOST_BROWSER */ } diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index 9f57c7c77c3b77..7c43883251134e 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -54,7 +54,7 @@ try { config.environmentVariables["MONO_LOG_LEVEL"] = "debug"; config.browserProfilerOptions = { sampleIntervalMs: 5, - // callSpec: "N:Sample" + callSpec: "N:Sample" // needs to match AOT profile }; }, preInit: () => { console.log('user code Module.preInit'); }, From 4be7e982d9b763718d43a7e7dcbb4d282deb7dfe Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 17 Feb 2025 15:33:31 +0100 Subject: [PATCH 15/25] Update src/mono/browser/runtime/types/internal.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/mono/browser/runtime/types/internal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index af942fc91bc2dc..8dd0e98d08e0a8 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -264,7 +264,7 @@ export type BrowserProfilerOptions = { * See callspec in https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md#trace-monovm-profiler-events-during-startup * When used together with Mono AOT, the callspec needs to match one in browser:callspec=N:Sample; in your project file. */ - callSpec?: number, + callSpec?: string, } export type LogProfilerOptions = { From e65b4d801435dd6efaaa4d0bba72b7a6e161e5d1 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 18 Feb 2025 10:40:22 +0100 Subject: [PATCH 16/25] feedback: - rename mono_jiterp_free_method_data_js to mono_wasm_free_method_data - use that in profiler --- src/mono/browser/runtime/exports-binding.ts | 4 ++-- src/mono/browser/runtime/jiterpreter.ts | 7 ++++++- src/mono/browser/runtime/profiler.ts | 5 +++++ src/mono/mono/mini/interp/jiterpreter.c | 4 ++-- src/mono/mono/mini/interp/jiterpreter.h | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 050c67cdc65c63..99f2142e2f0864 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug"; import { mono_wasm_release_cs_owned_object } from "./gc-handles"; import { mono_wasm_bind_js_import_ST, mono_wasm_invoke_js_function, mono_wasm_invoke_jsimport_MT, mono_wasm_invoke_jsimport_ST } from "./invoke-js"; -import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter"; +import { mono_interp_tier_prepare_jiterpreter, mono_wasm_free_method_data } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue } from "./jiterpreter-jit-call"; import { mono_wasm_resolve_or_reject_promise } from "./marshal-to-js"; @@ -68,7 +68,7 @@ export const mono_wasm_imports = [ mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, - mono_jiterp_free_method_data_js, + mono_wasm_free_method_data, mono_wasm_profiler_now, mono_wasm_profiler_record, diff --git a/src/mono/browser/runtime/jiterpreter.ts b/src/mono/browser/runtime/jiterpreter.ts index fa15904fa46492..80da42e655f6ad 100644 --- a/src/mono/browser/runtime/jiterpreter.ts +++ b/src/mono/browser/runtime/jiterpreter.ts @@ -26,6 +26,7 @@ import { mono_jiterp_free_method_data_interp_entry } from "./jiterpreter-interp- import { mono_jiterp_free_method_data_jit_call } from "./jiterpreter-jit-call"; import { mono_log_error, mono_log_info, mono_log_warn } from "./logging"; import { utf8ToString } from "./strings"; +import { mono_wasm_profiler_free_method } from "./profiler"; // Controls miscellaneous diagnostic output. export const trace = 0; @@ -1080,9 +1081,13 @@ export function mono_interp_tier_prepare_jiterpreter ( // NOTE: This will potentially be called once for every trace entry point // in a given method, not just once per method -export function mono_jiterp_free_method_data_js ( +export function mono_wasm_free_method_data ( method: MonoMethod, imethod: number, traceIndex: number ) { + if (runtimeHelpers.emscriptenBuildOptions.enablePerfTracing) { + mono_wasm_profiler_free_method(method); + } + // TODO: Uninstall the trace function pointer from the function pointer table, // so that the compiled trace module can be freed by the browser eventually // Release the trace info object, if present diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 5dbbc3d824160a..bcb285672acccc 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -80,6 +80,7 @@ export function startMeasure (): TimeStamp { export function endMeasure (start: TimeStamp, block: string, id?: string) { if (runtimeHelpers.enablePerfMeasure && start) { + // API is slightly different between web and Nodejs const options = ENVIRONMENT_IS_WEB ? { start: start as any } : { startTime: start as any }; @@ -92,6 +93,10 @@ export function mono_wasm_profiler_now (): number { return globalThis.performance.now(); } +export function mono_wasm_profiler_free_method (method: MonoMethod): void { + methodNames.delete(method as any); +} + const methodNames: Map = new Map(); export function mono_wasm_profiler_record (method: MonoMethod, start: number): void { const options = ENVIRONMENT_IS_WEB diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index cacc9041cab189..887038ca47fa64 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -977,7 +977,7 @@ mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod) JiterpreterOpcode *opcode = (JiterpreterOpcode *)p; guint32 trace_index = opcode->trace_index; need_extra_free = FALSE; - mono_jiterp_free_method_data_js (method, imethod, trace_index); + mono_wasm_free_method_data (method, imethod, trace_index); break; } } @@ -988,7 +988,7 @@ mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod) if (need_extra_free) { // HACK: Perform a single free operation to clear out any stuff from the jit queues // This will happen if we didn't encounter any jiterpreter traces in the method - mono_jiterp_free_method_data_js (method, imethod, 0); + mono_wasm_free_method_data (method, imethod, 0); } } diff --git a/src/mono/mono/mini/interp/jiterpreter.h b/src/mono/mono/mini/interp/jiterpreter.h index 9030da2c06a28e..078e10d77fa85c 100644 --- a/src/mono/mono/mini/interp/jiterpreter.h +++ b/src/mono/mono/mini/interp/jiterpreter.h @@ -163,7 +163,7 @@ mono_interp_invoke_wasm_jit_call_trampoline ( #ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__ extern void -mono_jiterp_free_method_data_js ( +mono_wasm_free_method_data ( MonoMethod *method, InterpMethod *imethod, int trace_index ); From 8cef96da771abf2d8b5f195faf8006bd7770ac5d Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 18 Feb 2025 11:52:59 +0100 Subject: [PATCH 17/25] - feedback - switch profiler_stack_frames to fixed size buffer --- src/mono/mono/mini/mini.c | 10 +-- src/mono/mono/profiler/browser.c | 67 ++++++++++++------- src/mono/sample/wasm/browser-advanced/main.js | 2 +- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index eb6b71cda081e4..ee764bbc969c97 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2765,14 +2765,14 @@ skip_insert_safepoint (MonoCompile *cfg) info->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced)) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for the polling function icall\n"); - return true; + return TRUE; } } if (cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for native-to-managed wrappers.\n"); - return true; + return TRUE; } if (cfg->method->wrapper_type == MONO_WRAPPER_OTHER) { @@ -2782,16 +2782,16 @@ skip_insert_safepoint (MonoCompile *cfg) /* These wrappers shouldn't do any icalls */ if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for interp-in wrappers.\n"); - return true; + return TRUE; } } if (cfg->method->wrapper_type == MONO_WRAPPER_WRITE_BARRIER) { if (cfg->verbose_level > 1) printf ("SKIPPING SAFEPOINTS for write barrier wrappers.\n"); - return true; + return TRUE; } - return false; + return FALSE; } /* diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index bffca70d214d5f..ff233a55a05f7a 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -30,7 +30,7 @@ struct _MonoProfiler { }; static MonoProfiler browser_profiler; -static int desired_sample_interval_ms; +static double desired_sample_interval_ms; static MonoCallSpec callspec; #ifdef HOST_BROWSER @@ -41,10 +41,13 @@ struct _ProfilerStackFrame { MonoMethod *method; double start; bool should_record; - ProfilerStackFrame *next; }; -static ProfilerStackFrame *profiler_stack; +enum { MAX_STACK_DEPTH = 100 }; +static ProfilerStackFrame profiler_stack_frames[MAX_STACK_DEPTH]; +// -1 means empty stack, we keep counting even after MAX_STACK_DEPTH is reached +static int top_stack_frame_index; + static double last_sample_time; static int prev_skips_per_period; static int skips_per_period; @@ -84,25 +87,33 @@ method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c sample_skip_counter++; stack_depth++; - ProfilerStackFrame* frame = g_new0 (ProfilerStackFrame, 1); - double now = frame->start = mono_wasm_profiler_now (); - frame->should_record = should_record_frame (now); - frame->next = profiler_stack; - frame->method = method; - profiler_stack = frame; + top_stack_frame_index++; + if (top_stack_frame_index < MAX_STACK_DEPTH) { + ProfilerStackFrame *newframe = &profiler_stack_frames[top_stack_frame_index]; + double now = newframe->start = mono_wasm_profiler_now (); + newframe->should_record = should_record_frame (now); + newframe->method = method; + } } static void method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(profiler_stack); + g_assert(top_stack_frame_index > 0); sample_skip_counter++; - if (!profiler_stack->should_record) + bool is_over = top_stack_frame_index >= MAX_STACK_DEPTH; + int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; + ProfilerStackFrame *top_frame = &profiler_stack_frames[top_index]; + + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter + g_assert(is_over || top_frame->method == method); + + if (!top_frame->should_record) { - profiler_stack->should_record = should_record_frame (mono_wasm_profiler_now ()); + top_frame->should_record = should_record_frame (mono_wasm_profiler_now ()); } } @@ -110,27 +121,31 @@ static void method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(profiler_stack); + g_assert(top_stack_frame_index >= 0); sample_skip_counter++; stack_depth--; - // pop top frame - ProfilerStackFrame *top = profiler_stack; - profiler_stack = profiler_stack->next; - + bool is_over = top_stack_frame_index >= MAX_STACK_DEPTH; + int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; + ProfilerStackFrame *top_frame = &profiler_stack_frames[top_index]; + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(top->method == method); + g_assert(is_over || top_frame->method == method); - if (top->should_record || should_record_frame (mono_wasm_profiler_now ())) + // pop top frame + top_stack_frame_index--; + + if (top_frame->should_record || should_record_frame (mono_wasm_profiler_now ())) { - // propagate keep to parent, if any - if(profiler_stack) - profiler_stack->should_record = TRUE; + // propagate should_record to parent, if any + if(top_index > 0) + { + profiler_stack_frames[top_index - 1].should_record = TRUE; + } - mono_wasm_profiler_record (method, top->start); + mono_wasm_profiler_record (method, top_frame->start); } - g_free (top); } static void @@ -209,7 +224,7 @@ parse_arg (const char *arg) } else if (match_option (arg, "interval", &val)) { char *end; - desired_sample_interval_ms = strtoul (val, &end, 10); + desired_sample_interval_ms = strtod (val, &end); } } @@ -293,7 +308,7 @@ mono_profiler_init_browser (const char *desc) } #ifdef HOST_BROWSER - profiler_stack = NULL; + top_stack_frame_index = -1; last_sample_time = 0; prev_skips_per_period = 1; skips_per_period = 1; diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index 7c43883251134e..94598646652fc7 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -53,7 +53,7 @@ try { // config is loaded and could be tweaked before the rest of the runtime startup sequence config.environmentVariables["MONO_LOG_LEVEL"] = "debug"; config.browserProfilerOptions = { - sampleIntervalMs: 5, + sampleIntervalMs: 5.15, callSpec: "N:Sample" // needs to match AOT profile }; }, From a9d2a849c052a3028e8018b501819d45b09ce437 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 20 Feb 2025 20:19:09 +0100 Subject: [PATCH 18/25] put samplepoint at the start of BB --- src/mono/mono/mini/mini.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index ee764bbc969c97..2be9b63d24f4ea 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2853,12 +2853,25 @@ static void insert_samplepoint (MonoCompile *cfg, MonoBasicBlock *bblock) { if (cfg->verbose_level > 1) - printf ("ADDING SAMPLE POINT TO BB %d\n", bblock->block_num); + printf ("ADDING SAMPLE POINT TO BB%d\n", bblock->block_num); + // store the previous instruction list and make the bb empty + MonoInst *begin = bblock->code; + MonoInst *end = bblock->last_ins; + bblock->code = bblock->last_ins = NULL; + + // insert the samplepoint at the start of the bb MonoBasicBlock *prev_cbb = cfg->cbb; cfg->cbb = bblock; mini_profiler_emit_samplepoint (cfg); cfg->cbb = prev_cbb; + + // append the previous instruction list to the end of the bb + if (begin) { + begin->prev = bblock->last_ins; + bblock->last_ins->next = begin; + bblock->last_ins = end; + } } static void From 27e5489fa4f075f21be3c78f5afc7dd8d515bb0e Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 20 Feb 2025 20:56:57 +0100 Subject: [PATCH 19/25] more --- src/mono/mono/profiler/browser.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index ff233a55a05f7a..f112cc8017f76c 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -52,7 +52,6 @@ static double last_sample_time; static int prev_skips_per_period; static int skips_per_period; static int sample_skip_counter; -static int stack_depth; double mono_wasm_profiler_now (); void mono_wasm_profiler_record (MonoMethod *method, double start); @@ -85,7 +84,6 @@ static void method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { sample_skip_counter++; - stack_depth++; top_stack_frame_index++; if (top_stack_frame_index < MAX_STACK_DEPTH) { @@ -100,7 +98,7 @@ static void method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(top_stack_frame_index > 0); + g_assert(top_stack_frame_index >= 0); sample_skip_counter++; @@ -124,7 +122,6 @@ method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c g_assert(top_stack_frame_index >= 0); sample_skip_counter++; - stack_depth--; bool is_over = top_stack_frame_index >= MAX_STACK_DEPTH; int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; From d517eb79b243a7cc99050ef08a58ce8feb762da6 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 20 Feb 2025 21:15:08 +0100 Subject: [PATCH 20/25] more --- src/mono/mono/profiler/browser.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index f112cc8017f76c..20919fe2fbc908 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -310,7 +310,6 @@ mono_profiler_init_browser (const char *desc) prev_skips_per_period = 1; skips_per_period = 1; sample_skip_counter = 1; - stack_depth = 0; // install this only in production run, not in AOT run mono_profiler_set_method_samplepoint_callback (handle, method_samplepoint); From cf07bc2f441938a5c46477074150fbd46ddfcf8f Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 24 Feb 2025 14:36:51 +0100 Subject: [PATCH 21/25] - add MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT variant without CONTEXT - fix flag detection for both variants - insert enter_profiling only when not inlining, to match leave event behavior --- src/mono/mono/mini/interp/interp.c | 2 +- src/mono/mono/mini/interp/transform.c | 22 ++++++++++--------- src/mono/mono/mini/mini-profiler.c | 6 ++--- src/mono/mono/mini/mini.c | 5 +++-- src/mono/mono/mini/mini.h | 2 +- .../mono/metadata/details/profiler-types.h | 6 ++++- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index b43e287c30171a..79d0b6d6295966 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3893,7 +3893,7 @@ interp_ldvirtftn_delegate (gpointer arg, MonoDelegate *del) #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))) { \ + (frame->imethod->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper ## _CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper)))) { \ MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);\ prof_ctx->interp_frame = frame;\ prof_ctx->method = frame->imethod->method; \ diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 301e6538045c0a..2a85a5262e8470 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -739,7 +739,7 @@ handle_branch (TransformData *td, int long_op, int offset) 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) + if (rtm->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT | MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT )) samplepoint_profiling |= PROFILING_FLAG; if (samplepoint_profiling) { interp_add_ins (td, MINT_PROF_SAMPLEPOINT); @@ -5376,14 +5376,16 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->has_localloc = TRUE; } - guint16 enter_profiling = 0; - if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) - enter_profiling |= TRACING_FLAG; - if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER) - enter_profiling |= PROFILING_FLAG; - if (enter_profiling) { - interp_add_ins (td, MINT_PROF_ENTER); - td->last_ins->data [0] = enter_profiling; + if(!inlining) { + guint16 enter_profiling = 0; + if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) + enter_profiling |= TRACING_FLAG; + if (rtm->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT)) + enter_profiling |= PROFILING_FLAG; + if (enter_profiling) { + interp_add_ins (td, MINT_PROF_ENTER); + td->last_ins->data [0] = enter_profiling; + } } /* @@ -5948,7 +5950,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, guint16 exit_profiling = 0; if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) exit_profiling |= TRACING_FLAG; - if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE) + if (rtm->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT)) exit_profiling |= PROFILING_FLAG; if (exit_profiling) { /* This does the return as well */ diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index b1164b8b9d2f76..14e4dcc0145c7e 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -81,7 +81,7 @@ mini_profiler_emit_enter (MonoCompile *cfg) gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if (!trace && (!MONO_CFG_PROFILE (cfg, ENTER) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) + if (!trace && (!(MONO_CFG_PROFILE (cfg, ENTER) || MONO_CFG_PROFILE (cfg, ENTER_CONTEXT)) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; @@ -109,7 +109,7 @@ mini_profiler_emit_samplepoint (MonoCompile *cfg) gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if (!trace && (!MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) + if (!trace && (!(MONO_CFG_PROFILE (cfg, SAMPLEPOINT) || MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT))|| (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; @@ -133,7 +133,7 @@ mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret) gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if (!trace && (!MONO_CFG_PROFILE (cfg, LEAVE) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) + if (!trace && (!(MONO_CFG_PROFILE (cfg, LEAVE) || MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT)) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 2be9b63d24f4ea..17d1f3638e1023 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3451,7 +3451,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts if (trace) cfg->prof_flags = (MonoProfilerCallInstrumentationFlags)( MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT | - MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT); + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT | + MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT | MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT); /* The debugger has no liveness information, so avoid sharing registers/stack slots */ if (mini_debug_options.mdb_optimizations || MONO_CFG_PROFILE_CALL_CONTEXT (cfg)) { @@ -3744,7 +3745,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts mono_cfg_dump_ir (cfg, "insert_safepoints"); } - if (MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) { + if (MONO_CFG_PROFILE (cfg, SAMPLEPOINT) || MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) { MONO_TIME_TRACK (mono_jit_stats.jit_insert_samplepoints, insert_samplepoints (cfg)); mono_cfg_dump_ir (cfg, "insert_samplepoints"); } diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 26ebd7f90fdf6b..9d4fb4fe2b109e 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1679,7 +1679,7 @@ typedef struct { G_UNLIKELY ((cfg)->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ ## flag) #define MONO_CFG_PROFILE_CALL_CONTEXT(cfg) \ - (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT) || MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT)) + (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT) || MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT) || MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT)) typedef enum { MONO_CFG_HAS_ALLOCA = 1 << 0, diff --git a/src/native/public/mono/metadata/details/profiler-types.h b/src/native/public/mono/metadata/details/profiler-types.h index 1a1c419e5d2097..8371766743550a 100644 --- a/src/native/public/mono/metadata/details/profiler-types.h +++ b/src/native/public/mono/metadata/details/profiler-types.h @@ -77,10 +77,14 @@ typedef enum { * Instrument exceptional method exits. */ MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE = 1 << 6, + /** + * Instrument method samplepoints + */ + MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT = 1 << 7, /** * Instrument method samplepoints with context. */ - MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT = 1 << 7, + MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT = 1 << 8, } MonoProfilerCallInstrumentationFlags; typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFilterCallback) (MonoProfiler *prof, MonoMethod *method); From c2fc83e0f2aae7fad98427dc7391ddb42a47af24 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 24 Feb 2025 16:41:33 +0100 Subject: [PATCH 22/25] fix for empty sample point block --- src/mono/mono/mini/mini-profiler.c | 2 +- src/mono/mono/mini/mini.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/mini-profiler.c b/src/mono/mono/mini/mini-profiler.c index 14e4dcc0145c7e..58b83a0d798456 100644 --- a/src/mono/mono/mini/mini-profiler.c +++ b/src/mono/mono/mini/mini-profiler.c @@ -109,7 +109,7 @@ mini_profiler_emit_samplepoint (MonoCompile *cfg) gboolean trace = mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method); - if (!trace && (!(MONO_CFG_PROFILE (cfg, SAMPLEPOINT) || MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT))|| (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) + if (!trace && (!(MONO_CFG_PROFILE (cfg, SAMPLEPOINT) || MONO_CFG_PROFILE (cfg, SAMPLEPOINT_CONTEXT)) || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))) return; MonoInst *iargs [3]; diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 17d1f3638e1023..203f84349eddce 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2868,9 +2868,14 @@ insert_samplepoint (MonoCompile *cfg, MonoBasicBlock *bblock) // append the previous instruction list to the end of the bb if (begin) { - begin->prev = bblock->last_ins; - bblock->last_ins->next = begin; - bblock->last_ins = end; + if(bblock->code) { + begin->prev = bblock->last_ins; + bblock->last_ins->next = begin; + bblock->last_ins = end; + } else { + bblock->code = begin; + bblock->last_ins = end; + } } } From e00fc07769bcc0fd42169a5ede0efccad2034944 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 24 Feb 2025 23:22:38 +0100 Subject: [PATCH 23/25] - fix raising profiler event for method_exception_leave in mono_handle_exception_internal - also when MONO_EXCEPTION_CLAUSE_NONE - more asserts in browser profiler --- src/mono/mono/mini/mini-exceptions.c | 46 ++++++++++++------------ src/mono/mono/profiler/browser.c | 53 +++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index 85f566553fa55e..4fce6945132167 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -2068,6 +2068,22 @@ mono_get_exception_runtime_wrapped_checked (MonoObject *wrapped_exception_raw, M HANDLE_FUNCTION_RETURN_OBJ (ret); } +#define MONO_PROFILER_RAISE_EXCEPTION_CLAUSE(clause_flags) \ + if (G_UNLIKELY (mono_profiler_clauses_enabled ())) { \ + jit_tls->orig_ex_ctx_set = TRUE; \ + MONO_PROFILER_RAISE (exception_clause, (method, i, clause_flags , ex_obj)); \ + jit_tls->orig_ex_ctx_set = FALSE; \ + } + +#define MONO_PROFILER_RAISE_EXCEPTION_LEAVE \ + if (MONO_PROFILER_ENABLED (method_exception_leave) && \ + mono_profiler_get_call_instrumentation_flags (method) & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE) { \ + jit_tls->orig_ex_ctx_set = TRUE; \ + MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj)); \ + jit_tls->orig_ex_ctx_set = FALSE; \ + } + + /** * mono_handle_exception_internal: * \param ctx saved processor state @@ -2437,11 +2453,9 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu * catch portion of this EH clause, pass MONO_EXCEPTION_CLAUSE_NONE explicitly * instead of ei->flags. */ - if (G_UNLIKELY (mono_profiler_clauses_enabled ())) { - jit_tls->orig_ex_ctx_set = TRUE; - MONO_PROFILER_RAISE (exception_clause, (method, i, MONO_EXCEPTION_CLAUSE_NONE, ex_obj)); - jit_tls->orig_ex_ctx_set = FALSE; - } + MONO_PROFILER_RAISE_EXCEPTION_CLAUSE ( MONO_EXCEPTION_CLAUSE_NONE ); + + MONO_PROFILER_RAISE_EXCEPTION_LEAVE; mini_set_abort_threshold (&frame); @@ -2493,22 +2507,17 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu if (mono_trace_is_enabled () && mono_trace_eval (method)) g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (method, TRUE)); - if (G_UNLIKELY (mono_profiler_clauses_enabled ())) { - jit_tls->orig_ex_ctx_set = TRUE; - MONO_PROFILER_RAISE (exception_clause, (method, i, (MonoExceptionEnum)ei->flags, ex_obj)); - jit_tls->orig_ex_ctx_set = FALSE; - } + MONO_PROFILER_RAISE_EXCEPTION_CLAUSE ((MonoExceptionEnum)ei->flags); + + MONO_PROFILER_RAISE_EXCEPTION_LEAVE; } if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) { if (mono_trace_is_enabled () && mono_trace_eval (method)) g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (method, TRUE)); - if (G_UNLIKELY (mono_profiler_clauses_enabled ())) { - jit_tls->orig_ex_ctx_set = TRUE; - MONO_PROFILER_RAISE (exception_clause, (method, i, (MonoExceptionEnum)ei->flags, ex_obj)); - jit_tls->orig_ex_ctx_set = FALSE; - } + MONO_PROFILER_RAISE_EXCEPTION_CLAUSE ((MonoExceptionEnum)ei->flags); + MONO_PROFILER_RAISE_EXCEPTION_LEAVE; } if (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) { mono_set_lmf (lmf); @@ -2558,13 +2567,6 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu } } - if (MONO_PROFILER_ENABLED (method_exception_leave) && - mono_profiler_get_call_instrumentation_flags (method) & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE) { - jit_tls->orig_ex_ctx_set = TRUE; - MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj)); - jit_tls->orig_ex_ctx_set = FALSE; - } - *ctx = new_ctx; } diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c index 20919fe2fbc908..9fd0eebdd164c1 100644 --- a/src/mono/mono/profiler/browser.c +++ b/src/mono/mono/profiler/browser.c @@ -24,6 +24,7 @@ #include #include #include +#include struct _MonoProfiler { gboolean verbose; @@ -39,11 +40,12 @@ typedef struct _ProfilerStackFrame ProfilerStackFrame; struct _ProfilerStackFrame { MonoMethod *method; + gpointer interp_frame; double start; bool should_record; }; -enum { MAX_STACK_DEPTH = 100 }; +enum { MAX_STACK_DEPTH = 600 }; static ProfilerStackFrame profiler_stack_frames[MAX_STACK_DEPTH]; // -1 means empty stack, we keep counting even after MAX_STACK_DEPTH is reached static int top_stack_frame_index; @@ -91,6 +93,7 @@ method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c double now = newframe->start = mono_wasm_profiler_now (); newframe->should_record = should_record_frame (now); newframe->method = method; + newframe->interp_frame = ctx ? ctx->interp_frame : NULL; } } @@ -106,8 +109,10 @@ method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallCont int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; ProfilerStackFrame *top_frame = &profiler_stack_frames[top_index]; - // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(is_over || top_frame->method == method); + if (!is_over) { + g_assertf(top_frame->method == method, "method_exc_leave: %d method mismatch top_frame %s != leave %s\n", top_stack_frame_index, mono_method_get_full_name (top_frame->method), mono_method_get_full_name (method)); + g_assertf(!ctx || !top_frame->interp_frame || top_frame->interp_frame == ctx->interp_frame, "method_exc_leave: %d interp_frame mismatch top_frame %p != leave %p\n", top_stack_frame_index, top_frame->interp_frame, ctx->interp_frame); + } if (!top_frame->should_record) { @@ -127,8 +132,10 @@ method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; ProfilerStackFrame *top_frame = &profiler_stack_frames[top_index]; - // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter - g_assert(is_over || top_frame->method == method); + if (!is_over) { + g_assertf(top_frame->method == method, "method_exc_leave: %d method mismatch top_frame %s != leave %s\n", top_stack_frame_index, mono_method_get_full_name (top_frame->method), mono_method_get_full_name (method)); + g_assertf(!ctx || !top_frame->interp_frame || top_frame->interp_frame == ctx->interp_frame, "method_exc_leave: %d interp_frame mismatch top_frame %p != leave %p\n", top_stack_frame_index, top_frame->interp_frame, ctx->interp_frame); + } // pop top frame top_stack_frame_index--; @@ -146,13 +153,41 @@ method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c } static void -tail_call (MonoProfiler *prof, MonoMethod *method, MonoMethod *target) +method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) { - method_leave (prof, method, NULL); + // enter/leave are not balanced, perhaps due to different callspecs between AOT and interpreter + g_assert(top_stack_frame_index >= 0); + + sample_skip_counter++; + + bool is_over = top_stack_frame_index >= MAX_STACK_DEPTH; + int top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; + ProfilerStackFrame *top_frame = &profiler_stack_frames[top_index]; + + if (top_frame->should_record || should_record_frame (mono_wasm_profiler_now ())) + { + // propagate should_record to parent, if any + if(top_index > 0) + { + profiler_stack_frames[top_index - 1].should_record = TRUE; + } + + mono_wasm_profiler_record (method, top_frame->start); + } + + // pop top frame + top_stack_frame_index--; + + is_over = top_stack_frame_index >= MAX_STACK_DEPTH; + if (!is_over) { + top_index = is_over ? MAX_STACK_DEPTH - 1 : top_stack_frame_index; + top_frame = &profiler_stack_frames[top_index]; + g_assertf(top_frame->method == method, "method_exc_leave: %d method mismatch top_frame %s != leave %s\n", top_stack_frame_index, mono_method_get_full_name (top_frame->method), mono_method_get_full_name (method)); + } } static void -method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) +tail_call (MonoProfiler *prof, MonoMethod *method, MonoMethod *target) { method_leave (prof, method, NULL); } @@ -166,7 +201,7 @@ method_filter (MonoProfiler *prof, MonoMethod *method) !mono_callspec_eval (method, &callspec)) return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; - return MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT_CONTEXT | + return MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | From 1c8c0aba2488074bf28cd7bf211d7f8d6bb8b990 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 24 Feb 2025 23:32:34 +0100 Subject: [PATCH 24/25] improve short names like .ctor --- src/mono/browser/runtime/cwraps.ts | 2 ++ src/mono/browser/runtime/driver.c | 16 ++++++++++++++++ src/mono/browser/runtime/profiler.ts | 4 +++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 056d9e6485a414..b2c6617278259f 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -72,6 +72,7 @@ const fn_signatures: SigLine[] = [ [true, "mono_wasm_f64_to_i52", "number", ["number", "number"]], [true, "mono_wasm_f64_to_u52", "number", ["number", "number"]], [true, "mono_wasm_method_get_name", "number", ["number"]], + [true, "mono_wasm_method_get_name_ex", "number", ["number"]], [true, "mono_wasm_method_get_full_name", "number", ["number"]], [true, "mono_wasm_gc_lock", "void", []], [true, "mono_wasm_gc_unlock", "void", []], @@ -195,6 +196,7 @@ export interface t_Cwraps { mono_wasm_f64_to_i52(destination: VoidPtr, value: number): I52Error; mono_wasm_f64_to_u52(destination: VoidPtr, value: number): I52Error; mono_wasm_method_get_name(method: MonoMethod): CharPtr; + mono_wasm_method_get_name_ex(method: MonoMethod): CharPtr; mono_wasm_method_get_full_name(method: MonoMethod): CharPtr; mono_wasm_gc_lock(): void; mono_wasm_gc_unlock(): void; diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index 94269d04b1ce1e..62a9c4eb3b2c7d 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -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) { + const char *res; + MONO_ENTER_GC_UNSAFE; + res = mono_method_get_name (method); + // starts with .ctor or .cctor + if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */ && strlen (res) < 7) { + char *res_ex = (char *) malloc (128); + snprintf (res_ex, 128,"%s.%s", mono_class_get_name (mono_method_get_class (method)), res); + res = res_ex; + } else { + res = strdup (res); + } + MONO_EXIT_GC_UNSAFE; + return res; +} + EMSCRIPTEN_KEEPALIVE float mono_wasm_get_f32_unaligned (const float *src) { return *src; } diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index e6a349cdb6df8d..223fea908c77ff 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -7,6 +7,7 @@ import { ENVIRONMENT_IS_WEB, mono_assert, runtimeHelpers } from "./globals"; import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions, LogProfilerOptions } from "./types/internal"; import { profiler_c_functions as cwraps } from "./cwraps"; import { utf8ToString } from "./strings"; +import { free } from "./memory"; // Initialize the AOT profiler with OPTIONS. // Requires the AOT profiler to be linked into the app. @@ -106,9 +107,10 @@ export function mono_wasm_profiler_record (method: MonoMethod, start: number): v : { startTime: start }; let methodName = methodNames.get(method as any); if (!methodName) { - const chars = cwraps.mono_wasm_method_get_name(method); + const chars = cwraps.mono_wasm_method_get_name_ex(method); methodName = utf8ToString(chars); methodNames.set(method as any, methodName); + free(chars as any); } globalThis.performance.measure(methodName, options); } From 83b49cdc3c0ae4b7fa08f5346aad0203742884dc Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 25 Feb 2025 09:14:56 +0100 Subject: [PATCH 25/25] feedback --- src/mono/mono/mini/interp/transform.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 2a85a5262e8470..573408c1e85e05 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -5376,16 +5376,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->has_localloc = TRUE; } - if(!inlining) { - guint16 enter_profiling = 0; - if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) - enter_profiling |= TRACING_FLAG; - if (rtm->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT)) - enter_profiling |= PROFILING_FLAG; - if (enter_profiling) { - interp_add_ins (td, MINT_PROF_ENTER); - td->last_ins->data [0] = enter_profiling; - } + guint16 enter_profiling = 0; + if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) + enter_profiling |= TRACING_FLAG; + if (rtm->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT)) + enter_profiling |= PROFILING_FLAG; + if (enter_profiling) { + interp_add_ins (td, MINT_PROF_ENTER); + td->last_ins->data [0] = enter_profiling; } /*