diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index e01f3e613c2798..642c727fd00a20 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -576,8 +576,6 @@ <_MonoRollupEnvironmentVariable Include="WasmEnableThreads:$(WasmEnableThreads)" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_SIMD:1" Condition="'$(WasmEnableSIMD)' != 'false'" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_SIMD:0" Condition="'$(WasmEnableSIMD)' == 'false'" /> - <_MonoRollupEnvironmentVariable Include="WASM_PERFTRACING:1" Condition="'$(WasmPerfTracing)' == 'true'" /> - <_MonoRollupEnvironmentVariable Include="WASM_PERFTRACING:0" Condition="'$(WasmPerfTracing)' != 'true'" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:1" Condition="'$(WasmEnableExceptionHandling)' != 'false'" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" /> <_MonoRollupEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE:1" Condition="'$(WasmEnableJsInteropByValue)' == 'true'" /> diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 20cb8cef23ec21..6e1516bfa620e6 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -148,6 +148,7 @@ + diff --git a/src/mono/browser/runtime/diagnostics/index.ts b/src/mono/browser/runtime/diagnostics/index.ts index 09c16da2d78959..38e432e896eac3 100644 --- a/src/mono/browser/runtime/diagnostics/index.ts +++ b/src/mono/browser/runtime/diagnostics/index.ts @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import type { GlobalObjects } from "../types/internal"; +import type { GlobalObjects, MonoMethod } from "../types/internal"; import type { CharPtr, VoidPtr } from "../types/emscripten"; -import { diagnosticHelpers, setRuntimeGlobalsImpl } from "./globals"; +import { diagnosticHelpers, runtimeHelpers, setRuntimeGlobalsImpl } from "./globals"; /* eslint-disable @typescript-eslint/no-unused-vars */ export function setRuntimeGlobals (globalObjects: GlobalObjects): void { @@ -30,8 +30,13 @@ export function setRuntimeGlobals (globalObjects: GlobalObjects): void { return 0; }; - diagnosticHelpers. ds_rt_websocket_close = (client_socket :number):number => { + diagnosticHelpers.ds_rt_websocket_close = (client_socket :number):number => { // Not implemented yet return 0; }; + runtimeHelpers.mono_wasm_instrument_method = (method: MonoMethod): number => { + const environmentVariables = runtimeHelpers.config.environmentVariables || {}; + const value = environmentVariables["DOTNET_WasmPerfInstrumentation"]; + return (value == "1" || value == "true") ? 1 : 0; + }; } diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index 62a9c4eb3b2c7d..bad04b732caa48 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -534,17 +534,16 @@ 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; +EMSCRIPTEN_KEEPALIVE char * mono_wasm_method_get_name_ex (MonoMethod *method) { + char *res; MONO_ENTER_GC_UNSAFE; - res = mono_method_get_name (method); + const char *method_name = 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; + res = (char *) malloc (128); + snprintf (res, 128,"%s.%s", mono_class_get_name (mono_method_get_class (method)), method_name); } else { - res = strdup (res); + res = strdup (method_name); } MONO_EXIT_GC_UNSAFE; return res; diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 31a050bd82f0cb..c91db1f79834ce 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -25,7 +25,7 @@ import { mono_wasm_dump_threads } from "./pthreads/ui-thread"; import { mono_wasm_schedule_synchronization_context } from "./pthreads/shared"; import { mono_wasm_get_locale_info } from "./globalization-locale"; -import { mono_wasm_profiler_record, mono_wasm_profiler_now } from "./profiler"; +import { mono_wasm_profiler_record, mono_wasm_profiler_now, mono_wasm_instrument_method } from "./profiler"; import { ds_rt_websocket_create, ds_rt_websocket_send, ds_rt_websocket_poll, ds_rt_websocket_recv, ds_rt_websocket_close } from "./diagnostics"; // the JS methods would be visible to EMCC linker and become imports of the WASM module @@ -72,7 +72,8 @@ export const mono_wasm_imports = [ mono_interp_flush_jitcall_queue, mono_wasm_free_method_data, - // browser.c + // browser.c, ep-rt-mono-runtime-provider.c + mono_wasm_instrument_method, mono_wasm_profiler_now, mono_wasm_profiler_record, diff --git a/src/mono/browser/runtime/jiterpreter-trace-generator.ts b/src/mono/browser/runtime/jiterpreter-trace-generator.ts index b777f01e285ba9..56d1329de5251f 100644 --- a/src/mono/browser/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/browser/runtime/jiterpreter-trace-generator.ts @@ -1422,9 +1422,12 @@ export function generateWasmBody ( // call C case MintOpcode.MINT_PROF_ENTER: case MintOpcode.MINT_PROF_SAMPLEPOINT: + append_profiler_event(builder, ip, opcode); + break; case MintOpcode.MINT_PROF_EXIT: case MintOpcode.MINT_PROF_EXIT_VOID: append_profiler_event(builder, ip, opcode); + ip = abort; 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 55b46e882a42f0..87ddbf8c7a8a50 100644 --- a/src/mono/browser/runtime/jiterpreter.ts +++ b/src/mono/browser/runtime/jiterpreter.ts @@ -1084,7 +1084,7 @@ export function mono_interp_tier_prepare_jiterpreter ( export function mono_wasm_free_method_data ( method: MonoMethod, imethod: number, traceIndex: number ) { - if (runtimeHelpers.emscriptenBuildOptions.enablePerfTracing) { + if (runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler) { mono_wasm_profiler_free_method(method); } diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts index 7cd570ee07d0e7..b2c6fae2d8a7ca 100644 --- a/src/mono/browser/runtime/profiler.ts +++ b/src/mono/browser/runtime/profiler.ts @@ -119,3 +119,7 @@ export function mono_wasm_profiler_record (method: MonoMethod, start: number): v } globalThis.performance.measure(methodName, options); } + +export function mono_wasm_instrument_method (method:MonoMethod):number { + return runtimeHelpers.mono_wasm_instrument_method(method); +} diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 61e82dc1eceb66..ce6aef7e1ea125 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -244,8 +244,9 @@ export type RuntimeHelpers = { dumpThreads: () => void, mono_wasm_print_thread_dump: () => void, utf8ToString: (ptr: CharPtr) => string, - mono_background_exec: () =>void; - mono_wasm_ds_exec: () =>void; + mono_background_exec: () => void; + mono_wasm_ds_exec: () => void, + mono_wasm_instrument_method: (method:MonoMethod) => number, } export type DiagnosticHelpers = { diff --git a/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c index d91281d3c92499..7f73389eb27305 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c +++ b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -1340,15 +1341,17 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( } static void -method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) +sample_current_thread_stack_trace () { + MonoContext ctx; MonoThreadInfo *thread_info = mono_thread_info_current (); SampleProfileStackWalkData stack_walk_data; SampleProfileStackWalkData *data= &stack_walk_data; THREAD_INFO_TYPE adapter = { { 0 } }; + MONO_INIT_CONTEXT_FROM_FUNC (&ctx, sample_current_thread_stack_trace); data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); - data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&ctx->context); + data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&ctx); data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; data->stack_walk_data.stack_contents = &data->stack_contents; data->stack_walk_data.top_frame = true; @@ -1357,7 +1360,8 @@ method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c data->stack_walk_data.runtime_invoke_frame = false; ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (sample_profiler_walk_managed_stack_for_thread_callback, &ctx->context, MONO_UNWIND_NONE, &stack_walk_data); + // because this is single threaded, MONO_UNWIND_NONE is safe to use + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (sample_profiler_walk_managed_stack_for_thread_callback, &ctx, MONO_UNWIND_NONE, &stack_walk_data); if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; } @@ -1376,13 +1380,90 @@ method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c } } +static double desired_sample_interval_ms; + +static double last_sample_time; +static int prev_skips_per_period; +static int skips_per_period; +static int sample_skip_counter; + +#ifdef HOST_BROWSER +double mono_wasm_profiler_now (); +static double profiler_now () +{ + return mono_wasm_profiler_now (); +} +#else +#error "Not implemented" +#endif + +static void update_sample_frequency () +{ + // timer resolution in non-isolated contexts: 100 microseconds (decimal number) + double now = profiler_now (); + 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; +} + +static void +method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) +{ + sample_skip_counter++; + if (G_LIKELY(sample_skip_counter < skips_per_period)) + return; + update_sample_frequency (); + sample_current_thread_stack_trace (); +} + +static void +method_samplepoint (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) +{ + sample_skip_counter++; + if (G_LIKELY(sample_skip_counter < skips_per_period)) + return; + update_sample_frequency (); + sample_current_thread_stack_trace (); +} + +static void +method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) +{ + sample_skip_counter++; + if (G_LIKELY(sample_skip_counter < skips_per_period)) + return; + update_sample_frequency (); + sample_current_thread_stack_trace (); +} + +#ifdef HOST_BROWSER +int mono_wasm_instrument_method (); + static MonoProfilerCallInstrumentationFlags method_filter (MonoProfiler *prof, MonoMethod *method) { - // TODO add more instrumentation, something like MINT_SDB_SEQ_POINT - return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER; + if (!mono_wasm_instrument_method (method)){ + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + } + + return MONO_PROFILER_CALL_INSTRUMENTATION_SAMPLEPOINT | + MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; } +#else +#error "Not implemented" +#endif void ep_rt_mono_sampling_provider_component_init (void) @@ -1404,17 +1485,31 @@ ep_rt_mono_sampling_provider_component_fini (void) void ep_rt_mono_sample_profiler_enabled (EventPipeEvent *sampling_event) { + desired_sample_interval_ms = ((double)ep_sample_profiler_get_sampling_rate ()) / 1000000.0; + EP_ASSERT (desired_sample_interval_ms >= 0.0); + EP_ASSERT (desired_sample_interval_ms < 1000.0); + current_sampling_event = sampling_event; current_sampling_thread = ep_rt_thread_get_handle (); EP_ASSERT (_ep_rt_mono_sampling_profiler_provider != NULL); + + last_sample_time = 0; + prev_skips_per_period = 1; + skips_per_period = 1; + sample_skip_counter = 1; + + mono_profiler_set_method_samplepoint_callback (_ep_rt_mono_sampling_profiler_provider, method_samplepoint); mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, method_enter); + mono_profiler_set_method_exception_leave_callback (_ep_rt_mono_sampling_profiler_provider, method_exc_leave); } void ep_rt_mono_sample_profiler_disabled (void) { EP_ASSERT (_ep_rt_mono_sampling_profiler_provider != NULL); + mono_profiler_set_method_samplepoint_callback (_ep_rt_mono_sampling_profiler_provider, NULL); mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, NULL); + mono_profiler_set_method_exception_leave_callback (_ep_rt_mono_sampling_profiler_provider, NULL); } #endif // PERFTRACING_DISABLE_THREADS diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index d2d1dce5dc66d8..1a25e2e29746ee 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -776,6 +776,7 @@ inline void ep_rt_wait_event_free (ep_rt_wait_event_handle_t *wait_event) { + wait_event->event = NULL; } static diff --git a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c index 64c7202c787e4e..dcad1f2f88353c 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c @@ -61,7 +61,7 @@ buffer_manager_fini ( // buffer_manager owned by session. EP_ASSERT (buffer_manager == NULL || buffer_manager == ep_session_get_buffer_manager (session)); - ep_session_free (session); + ep_session_dec_ref (session); } static diff --git a/src/mono/mono/eventpipe/test/ep-buffer-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-tests.c index c4c02704a163be..d1eb3eb56f5d37 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-tests.c @@ -55,7 +55,7 @@ load_buffer_with_events_fini ( { ep_event_free (ep_event); ep_delete_provider (provider); - ep_session_free (session); + ep_session_dec_ref (session); } static diff --git a/src/mono/mono/eventpipe/test/ep-session-tests.c b/src/mono/mono/eventpipe/test/ep-session-tests.c index 5c09181c1d0075..0c2506eab62e0e 100644 --- a/src/mono/mono/eventpipe/test/ep-session-tests.c +++ b/src/mono/mono/eventpipe/test/ep-session-tests.c @@ -57,7 +57,7 @@ test_create_delete_session (void) ep_raise_error_if_nok (test_session != NULL); ep_on_exit: - ep_session_free (test_session); + ep_session_dec_ref (test_session); ep_provider_config_fini (current_provider_config); return result; @@ -141,7 +141,7 @@ test_add_session_providers (void) EP_LOCK_EXIT (section5) ep_on_exit: - ep_session_free (test_session); + ep_session_dec_ref (test_session); ep_provider_config_fini (current_provider_config); return result; @@ -230,7 +230,7 @@ test_session_special_get_set (void) } ep_on_exit: - ep_session_free (test_session); + ep_session_dec_ref (test_session); ep_provider_config_fini (current_provider_config); return result; diff --git a/src/mono/mono/eventpipe/test/ep-thread-tests.c b/src/mono/mono/eventpipe/test/ep-thread-tests.c index eebd10673fe794..77342b7633f250 100644 --- a/src/mono/mono/eventpipe/test/ep-thread-tests.c +++ b/src/mono/mono/eventpipe/test/ep-thread-tests.c @@ -446,7 +446,7 @@ test_thread_session_state (void) ep_thread_delete_session_state (thread, session); ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); } - ep_session_free (session); + ep_session_dec_ref (session); ep_provider_config_fini (provider_config); ep_thread_release (thread); return result; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 79d0b6d6295966..e37a23dcc96ad1 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3891,21 +3891,61 @@ interp_ldvirtftn_delegate (gpointer arg, MonoDelegate *del) return imethod_to_ftnptr (imethod, need_unbox); } +static MONO_NEVER_INLINE void +mono_interp_trace_with_ctx (InterpFrame *frame, void (*trace_cb)(MonoMethod*,MonoJitInfo*,MonoProfilerCallContext*)) +{ + MonoProfilerCallContext prof_ctx; + MonoLMFExt ext; + memset (&prof_ctx, 0, sizeof (MonoProfilerCallContext)); + prof_ctx.interp_frame = frame; + prof_ctx.method = frame->imethod->method; + prof_ctx.return_value = frame->retval; + interp_push_lmf (&ext, frame); + trace_cb (frame->imethod->method, frame->imethod->jinfo, &prof_ctx); + interp_pop_lmf (&ext); +} + +static MONO_NEVER_INLINE void +mono_interp_profiler_raise_with_ctx (InterpFrame *frame, void (*prof_cb)(MonoMethod*,MonoProfilerCallContext*)) +{ + MonoProfilerCallContext prof_ctx; + MonoLMFExt ext; + memset (&prof_ctx, 0, sizeof (MonoProfilerCallContext)); + prof_ctx.interp_frame = frame; + prof_ctx.method = frame->imethod->method; + prof_ctx.return_value = frame->retval; + interp_push_lmf (&ext, frame); + prof_cb (frame->imethod->method, &prof_ctx); + interp_pop_lmf (&ext); +} + +static MONO_NEVER_INLINE void +mono_interp_profiler_raise (InterpFrame *frame, void (*prof_cb)(MonoMethod*,MonoProfilerCallContext*)) +{ + MonoLMFExt ext; + interp_push_lmf (&ext, frame); + prof_cb (frame->imethod->method, NULL); + interp_pop_lmf (&ext); +} + +static MONO_NEVER_INLINE void +mono_interp_profiler_raise_tail_call (InterpFrame *frame, MonoMethod *new_method) +{ + MonoLMFExt ext; + interp_push_lmf (&ext, frame); + mono_profiler_raise_method_tail_call (frame->imethod->method, new_method); + interp_pop_lmf (&ext); +} + #define INTERP_PROFILER_RAISE(name_lower, name_upper) \ if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_ ## name_lower) && \ - (frame->imethod->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper ## _CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper)))) { \ - MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);\ - prof_ctx->interp_frame = frame;\ - prof_ctx->method = frame->imethod->method; \ - if (!is_void) \ - prof_ctx->return_value = frame->retval; \ + (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ ## name_upper ## _CONTEXT ))) { \ if (flag & TRACING_FLAG) \ - mono_trace_ ## name_lower ## _method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); \ + mono_interp_trace_with_ctx (frame, mono_trace_ ## name_lower ## _method); \ if (flag & PROFILING_FLAG) \ - MONO_PROFILER_RAISE (method_ ## name_lower, (frame->imethod->method, prof_ctx)); \ - g_free (prof_ctx); \ + mono_interp_profiler_raise_with_ctx (frame, mono_profiler_raise_method_ ## name_lower); \ } else if ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_ ## name_lower)) { \ - MONO_PROFILER_RAISE (method_ ## name_lower, (frame->imethod->method, NULL)); \ + mono_interp_profiler_raise (frame, mono_profiler_raise_method_ ## name_lower); \ } @@ -4134,7 +4174,8 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause } if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL) - MONO_PROFILER_RAISE (method_tail_call, (frame->imethod->method, new_method->method)); + if (MONO_PROFILER_ENABLED (method_tail_call)) + mono_interp_profiler_raise_tail_call (frame, new_method->method); if (!new_method->transformed) { MonoException *transform_ex = do_transform_method (new_method, frame, context); @@ -7628,16 +7669,12 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_CASE(MINT_PROF_ENTER) { guint16 flag = ip [1]; ip += 2; - gboolean is_void = TRUE; - // FIXME push/pop LMF INTERP_PROFILER_RAISE(enter, ENTER); MINT_IN_BREAK; } MINT_IN_CASE(MINT_PROF_SAMPLEPOINT) { guint16 flag = ip [1]; ip += 2; - gboolean is_void = TRUE; - // FIXME push/pop LMF INTERP_PROFILER_RAISE(samplepoint, SAMPLEPOINT); MINT_IN_BREAK; } @@ -9161,7 +9198,6 @@ mono_jiterp_get_polling_required_address () EMSCRIPTEN_KEEPALIVE void mono_jiterp_prof_enter (InterpFrame *frame, guint16 *ip) { - gboolean is_void = TRUE; guint16 flag = ip [1]; INTERP_PROFILER_RAISE(enter, ENTER); } @@ -9170,7 +9206,6 @@ EMSCRIPTEN_KEEPALIVE void mono_jiterp_prof_samplepoint (InterpFrame *frame, guint16 *ip) { guint16 flag = ip [1]; - gboolean is_void = TRUE; INTERP_PROFILER_RAISE(samplepoint, SAMPLEPOINT); } diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props index 84b0ab729466b0..6e1d190b53b467 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props @@ -3,6 +3,7 @@ false false false + false true true false diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets index ed1372de5c9103..548a6ce828c8d0 100644 --- a/src/mono/sample/wasm/Directory.Build.targets +++ b/src/mono/sample/wasm/Directory.Build.targets @@ -2,6 +2,7 @@ true + true @@ -11,8 +12,10 @@ $([MSBuild]::NormalizePath('$(WasmAppDir)', '$(RunScriptOutputName)')) + + <_ServeHeaders>$(_ServeHeaders) -h "Timing-Allow-Origin:*" - <_ServeHeaders Condition="'$(WasmEnableThreads)' == 'true'">$(_ServeHeaders) -h Cross-Origin-Embedder-Policy:require-corp -h Cross-Origin-Opener-Policy:same-origin + <_ServeHeaders Condition="'$(WasmEnableThreads)' == 'true'">$(_ServeHeaders) -h Cross-Origin-Embedder-Policy:require-corp -h Cross-Origin-Opener-Policy:same-origin <_ServeMimeTypes>$(_ServeMimeTypes) --mime .wasm=application/wasm diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index c1f76d7d9e0f11..4a1b5f4356f8f5 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -20,10 +20,15 @@ Defaults to true. - $(WasmEmitSymbolMap) - Generates a `dotnet.native.js.symbols` file with a map of wasm function number to name. - $(WasmEmitSourceMap) - Generates `dotnet.runtime.js.map` and `dotnet.js.map` files with a TypeScript source map. - - $(WasmDedup) - Whether to dedup generic instances when using AOT. Defaults to true. + - $(WasmDedup) - Whether to dedup generic instances when using AOT. Defaults to true. + + - $(WasmPerfTracing) - Enables diagnostic server tracing. Defaults to false. + - $(EventSourceSupport) - Enables EventSource support. Defaults to false. + - $(WasmPerfInstrumentation) - Enables CPU sampling instrumentation for diagnostic server. Defaults to false. + - $(MetricsSupport) - System.Diagnostics.Metrics support. Defaults to false. + - $(WasmProfilers) - Mono Profilers to use. Defaults to empty. Could be `log` and `browser`. + - $(AOTProfilePath) - profile data file to be used for profile-guided optimization - - $(WasmProfilers) - Profilers to use - - $(AOTProfilePath) - profile data file to be used for profile-guided optimization - $(InvariantGlobalization) - Whether to disable ICU. Defaults to false. - $(InvariantTimezone) - Whether to disable Timezone database. Defaults to false. @@ -183,6 +188,7 @@ false true _framework + false + + true + + + true + + + + true + + + + true ``` -In Blazor, you can customize the startup in your index.html -```html - - -``` +`Timing-Allow-Origin` HTTP header allows for more precise time measurements. -In simple browser template, you can add following to your `main.js` +### Profiling in the browser dev tools -```javascript -import { dotnet } from './dotnet.js' -await dotnet.withConfig({browserProfilerOptions: {}}).run(); +You can enable integration with the profiler in browser dev tools via following elements in your .csproj +```xml + + browser; + ``` ### Log Profiling for Memory Troubleshooting @@ -447,11 +446,3 @@ class Profiling Invoke `MyApp.Profiling.TakeHeapshot()` from your code in order to create a memory heap shot and flush the contents of the profile to the VFS. Make sure to align the namespace and class of the `logProfilerOptions.takeHeapshot` with your class. You can download the mpld file to analyze it. - -### Diagnostic tools - -We have initial implementation of diagnostic server and [event pipe](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe) - -At the moment it requires multi-threaded build of the runtime. - -For more details see [diagnostic-server.md](../browser/runtime/diagnostics/diagnostic-server.md) diff --git a/src/native/eventpipe/ep-session.c b/src/native/eventpipe/ep-session.c index 74395276d9e11d..a8b23f8402e759 100644 --- a/src/native/eventpipe/ep-session.c +++ b/src/native/eventpipe/ep-session.c @@ -93,6 +93,7 @@ static size_t streaming_loop_tick(EventPipeSession *const session) { bool ok; if (!ep_session_get_streaming_enabled (session)){ session->streaming_thread = NULL; + ep_session_dec_ref (session); return 1; // done } EP_GCX_PREEMP_ENTER @@ -126,6 +127,7 @@ session_create_streaming_thread (EventPipeSession *session) if (!ep_rt_thread_create ((void *)streaming_thread, (void *)session, EP_THREAD_TYPE_SESSION, &thread_id)) EP_UNREACHABLE ("Unable to create stream flushing thread."); #else + ep_session_inc_ref (session); ep_rt_volatile_store_uint32_t (&session->started, 1); ep_rt_queue_job ((void *)streaming_loop_tick, (void *)session); #endif @@ -184,6 +186,7 @@ ep_session_alloc ( EventPipeSession *instance = ep_rt_object_alloc (EventPipeSession); ep_raise_error_if_nok (instance != NULL); + ep_session_inc_ref (instance); instance->providers = ep_session_provider_list_alloc (providers, providers_len); ep_raise_error_if_nok (instance->providers != NULL); @@ -247,7 +250,7 @@ ep_session_alloc ( ep_on_error: ep_file_stream_writer_free (file_stream_writer); ep_ipc_stream_writer_free (ipc_stream_writer); - ep_session_free (instance); + ep_session_dec_ref (instance); instance = NULL; ep_exit_error_handler (); @@ -296,12 +299,21 @@ ep_session_remove_dangling_session_states (EventPipeSession *session) } void -ep_session_free (EventPipeSession *session) +ep_session_inc_ref (EventPipeSession *session) +{ + ep_rt_atomic_inc_uint32_t (&session->ref_count); +} + +void +ep_session_dec_ref (EventPipeSession *session) { ep_return_void_if_nok (session != NULL); EP_ASSERT (!ep_session_get_streaming_enabled (session)); + if (ep_rt_atomic_dec_uint32_t (&session->ref_count) != 0) + return; + ep_rt_wait_event_free (&session->rt_thread_shutdown_event); ep_session_provider_list_free (session->providers); diff --git a/src/native/eventpipe/ep-session.h b/src/native/eventpipe/ep-session.h index 646c4ab566c083..90b733751301d5 100644 --- a/src/native/eventpipe/ep-session.h +++ b/src/native/eventpipe/ep-session.h @@ -67,6 +67,8 @@ struct _EventPipeSession_Internal { bool enable_stackwalk; // Indicate that session is fully running (streaming thread started). volatile uint32_t started; + // Reference count for the session. This is used to track the number of references to the session. + volatile uint32_t ref_count; }; #if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_SESSION_GETTER_SETTER) @@ -101,7 +103,10 @@ ep_session_alloc ( void *callback_additional_data); void -ep_session_free (EventPipeSession *session); +ep_session_inc_ref (EventPipeSession *session); + +void +ep_session_dec_ref (EventPipeSession *session); // _Requires_lock_held (ep) EventPipeSessionProvider * diff --git a/src/native/eventpipe/ep.c b/src/native/eventpipe/ep.c index d57a01d9c0978d..45e853febed841 100644 --- a/src/native/eventpipe/ep.c +++ b/src/native/eventpipe/ep.c @@ -560,7 +560,7 @@ enable ( return session_id; ep_on_error: - ep_session_free (session); + ep_session_dec_ref (session); session_id = 0; ep_exit_error_handler (); } @@ -641,7 +641,7 @@ disable_holding_lock ( // been emitted. ep_session_write_sequence_point_unbuffered (session); - ep_session_free (session); + ep_session_dec_ref (session); // Providers can't be deleted during tracing because they may be needed when serializing the file. // Allow delete deferred providers to accumulate to mitigate potential use-after-free should @@ -1430,7 +1430,12 @@ ep_init (void) ep_rt_init_providers_and_events (); // Set the sampling rate for the sample profiler. + +#ifndef PERFTRACING_DISABLE_THREADS const uint32_t default_profiler_sample_rate_in_nanoseconds = 1000000; // 1 msec. +#else // PERFTRACING_DISABLE_THREADS + const uint32_t default_profiler_sample_rate_in_nanoseconds = 5000000; // 5 msec. +#endif // PERFTRACING_DISABLE_THREADS ep_sample_profiler_set_sampling_rate (default_profiler_sample_rate_in_nanoseconds); _ep_deferred_enable_session_ids = dn_vector_alloc_t (EventPipeSessionID);