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);