diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
index 07d77161002bc..b0e6a249f89ee 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
@@ -210,6 +210,7 @@
+
diff --git a/src/mono/mono.proj b/src/mono/mono.proj
index 38731e968660a..56bc7ebaa9179 100644
--- a/src/mono/mono.proj
+++ b/src/mono/mono.proj
@@ -905,6 +905,9 @@
<_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-profiler-aot.a">
$(RuntimeBinDir)libmono-profiler-aot.a
+ <_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-profiler-browser.a">
+ $(RuntimeBinDir)libmono-profiler-browser.a
+
<_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-wasm-eh-js.a">
$(RuntimeBinDir)libmono-wasm-eh-js.a
diff --git a/src/mono/mono/metadata/profiler.c b/src/mono/mono/metadata/profiler.c
index fe1b6b594bf37..d632fc3b6f62e 100644
--- a/src/mono/mono/metadata/profiler.c
+++ b/src/mono/mono/metadata/profiler.c
@@ -22,6 +22,10 @@ typedef void (*MonoProfilerInitializer) (const char *);
#define OLD_INITIALIZER_NAME "mono_profiler_startup"
#define NEW_INITIALIZER_NAME "mono_profiler_init"
+#if defined(TARGET_WASM) && defined(MONO_CROSS_COMPILE)
+MONO_API void mono_profiler_init_browser (const char *desc);
+#endif
+
static gboolean
load_profiler (MonoDl *module, const char *name, const char *desc)
{
@@ -148,6 +152,9 @@ load_profiler_from_installation (const char *libname, const char *name, const ch
*
* This function may \b only be called by embedders prior to running managed
* code.
+ *
+ * This could could be triggered by \c MONO_PROFILE env variable in normal mono process or
+ * by \c --profile=foo argument to mono-aot-cross.exe command line.
*/
void
mono_profiler_load (const char *desc)
@@ -171,6 +178,15 @@ mono_profiler_load (const char *desc)
mname = g_strdup (desc);
}
+#if defined(TARGET_WASM) && defined(MONO_CROSS_COMPILE)
+ // this code could be running as part of mono-aot-cross.exe
+ // in case of WASM we staticaly link in the browser.c profiler plugin
+ if(strcmp (mname, "browser") == 0) {
+ mono_profiler_init_browser (desc);
+ goto done;
+ }
+#endif
+
if (load_profiler_from_executable (mname, desc))
goto done;
diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt
index 9e74fd9e16fb4..c68af8beb34ca 100644
--- a/src/mono/mono/mini/CMakeLists.txt
+++ b/src/mono/mono/mini/CMakeLists.txt
@@ -325,7 +325,13 @@ else()
set(llvm_runtime_sources)
endif()
-set(mini_sources "main-core.c;${mini_common_sources};${arch_sources};${os_sources};${mini_interp_sources};${llvm_sources};${debugger_sources};${llvm_runtime_sources}")
+if(TARGET_WASM AND MONO_CROSS_COMPILE)
+set(profiler_sources ../profiler/browser.c)
+else()
+set(profiler_sources "")
+endif()
+
+set(mini_sources "main-core.c;${mini_common_sources};${arch_sources};${os_sources};${mini_interp_sources};${llvm_sources};${debugger_sources};${profiler_sources};${llvm_runtime_sources}")
if(LLVM_INCLUDEDIR)
include_directories(BEFORE SYSTEM "${LLVM_INCLUDEDIR}")
diff --git a/src/mono/mono/profiler/CMakeLists.txt b/src/mono/mono/profiler/CMakeLists.txt
index 4f860a1c8267e..cd5bf36dfa67d 100644
--- a/src/mono/mono/profiler/CMakeLists.txt
+++ b/src/mono/mono/profiler/CMakeLists.txt
@@ -35,4 +35,11 @@ if(NOT DISABLE_LIBS)
set_target_properties(mono-profiler-aot-static PROPERTIES OUTPUT_NAME mono-profiler-aot)
install(TARGETS mono-profiler-aot-static LIBRARY)
endif()
+
+ if(HOST_WASM)
+ add_library(mono-profiler-browser-static STATIC browser.c)
+ target_link_libraries(mono-profiler-browser-static monoapi)
+ set_target_properties(mono-profiler-browser-static PROPERTIES OUTPUT_NAME mono-profiler-browser)
+ install(TARGETS mono-profiler-browser-static LIBRARY)
+ endif()
endif()
diff --git a/src/mono/mono/profiler/browser.c b/src/mono/mono/profiler/browser.c
new file mode 100644
index 0000000000000..8140b03b535d8
--- /dev/null
+++ b/src/mono/mono/profiler/browser.c
@@ -0,0 +1,107 @@
+/*
+ * browser.c: Exporting profiler events to browser dev tools.
+
+ * Copyright 2022 Microsoft Corporation
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+*/
+
+#include
+
+#ifdef TARGET_WASM
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct _MonoProfiler {
+ gboolean verbose;
+};
+
+static MonoProfiler browser_profiler;
+
+#ifdef HOST_WASM
+
+void
+mono_wasm_profiler_enter ();
+
+void
+mono_wasm_profiler_leave (MonoMethod *method);
+
+static void
+method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx)
+{
+ mono_wasm_profiler_enter ();
+}
+
+static void
+method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx)
+{
+ mono_wasm_profiler_leave (method);
+}
+
+static void
+tail_call (MonoProfiler *prof, MonoMethod *method, MonoMethod *target)
+{
+ method_leave (prof, method, NULL);
+}
+
+static void
+method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc)
+{
+ method_leave (prof, method, NULL);
+}
+
+#endif /* HOST_WASM */
+
+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;
+}
+
+
+MONO_API void
+mono_profiler_init_browser (const char *desc);
+
+/**
+ * mono_profiler_init_browser:
+ * the entry point
+ */
+void
+mono_profiler_init_browser (const char *desc)
+{
+ MonoProfilerHandle handle = mono_profiler_create (&browser_profiler);
+
+ mono_profiler_set_call_instrumentation_filter_callback (handle, method_filter);
+
+ if (mono_jit_aot_compiling ()) {
+ return;
+ }
+
+#ifdef HOST_WASM
+ // install this only in production run, not in AOT run
+ 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 /* TARGET_WASM */
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 f74fdad80fad3..ef4c5f2dda2f8 100644
--- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
+++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
@@ -8,6 +8,8 @@
-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'"
+
+ browser;
diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js
index 37a66a8f17d68..296de716612da 100644
--- a/src/mono/sample/wasm/browser-advanced/main.js
+++ b/src/mono/sample/wasm/browser-advanced/main.js
@@ -19,7 +19,8 @@ try {
// This is called during emscripten `dotnet.wasm` instantiation, after we fetched config.
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.environmentVariables["MONO_LOG_LEVEL"] = "debug";
+ config.browserProfilerOptions = {};
},
preInit: () => { console.log('user code Module.preInit'); },
preRun: () => { console.log('user code Module.preRun'); },
diff --git a/src/mono/sample/wasm/browser-profile/Makefile b/src/mono/sample/wasm/browser-profile/Makefile
index 491ffaf8ba8a5..3374e9c2c8d19 100644
--- a/src/mono/sample/wasm/browser-profile/Makefile
+++ b/src/mono/sample/wasm/browser-profile/Makefile
@@ -3,7 +3,7 @@ TOP=../../../../..
include ../wasm.mk
PROJECT_NAME=Wasm.BrowserProfile.Sample.csproj
-BUILD_ARGS=/p:WasmBuildNative=true /p:EnableProfiler=true
+BUILD_ARGS=/p:WasmBuildNative=true
BUILD_PROFILED_ARGS=/p:RunAOTCompilation=true /p:AOTProfilePath=$(PROFILE_PATH)
run: run-browser
diff --git a/src/mono/sample/wasm/browser-profile/Wasm.BrowserProfile.Sample.csproj b/src/mono/sample/wasm/browser-profile/Wasm.BrowserProfile.Sample.csproj
index f6508c948e44a..7319738a6a0e1 100644
--- a/src/mono/sample/wasm/browser-profile/Wasm.BrowserProfile.Sample.csproj
+++ b/src/mono/sample/wasm/browser-profile/Wasm.BrowserProfile.Sample.csproj
@@ -4,10 +4,10 @@
main.js
aot;
true
+ false
-
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index 709be4e83f99f..c59baff0684dd 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -215,6 +215,7 @@
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DENABLE_AOT_PROFILER=1" Condition="$(WasmProfilers.Contains('aot'))" />
+ <_EmccCFlags Include="-DENABLE_BROWSER_PROFILER=1" Condition="$(WasmProfilers.Contains('browser'))" />
<_EmccCFlags Include="-DCORE_BINDINGS" />
<_EmccCFlags Include="-DGEN_PINVOKE=1" />
<_EmccCFlags Include="-emit-llvm" />
@@ -598,6 +599,7 @@
AOTProfilePath="$(AOTProfilePath)"
AotModulesTablePath="$(_DriverGenCPath)"
UseLLVM="true"
+ Profilers="$(WasmProfilers)"
DisableParallelAot="$(DisableParallelAot)"
DedupAssembly="$(_WasmDedupAssembly)"
CacheFilePath="$(_AOTCompilerCacheFile)"
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index a2031cab5457f..b4ab2787d2173 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -75,7 +75,7 @@
- @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle.
- @(WasmExtraConfig) - json elements to add to `mono-config.json`
- Eg.
+ Eg.
- Value attribute can have a number, bool, quoted string, or json string
diff --git a/src/mono/wasm/runtime/CMakeLists.txt b/src/mono/wasm/runtime/CMakeLists.txt
index c852a7df1df61..47e5d038ceccc 100644
--- a/src/mono/wasm/runtime/CMakeLists.txt
+++ b/src/mono/wasm/runtime/CMakeLists.txt
@@ -25,6 +25,7 @@ target_link_libraries(dotnet
${MONO_ARTIFACTS_DIR}/libmono-icall-table.a
${MONO_ARTIFACTS_DIR}/libmono-wasm-eh-js.a
${MONO_ARTIFACTS_DIR}/libmono-profiler-aot.a
+ ${MONO_ARTIFACTS_DIR}/libmono-profiler-browser.a
${NATIVE_BIN_DIR}/libSystem.Native.a
${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a)
diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts
index 80d6342910293..6deb4c3652786 100644
--- a/src/mono/wasm/runtime/assets.ts
+++ b/src/mono/wasm/runtime/assets.ts
@@ -6,6 +6,7 @@ import { mono_wasm_load_icu_data } from "./icu";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./imports";
import { mono_wasm_load_bytes_into_heap } from "./memory";
import { MONO } from "./net6-legacy/imports";
+import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { createPromiseController, PromiseAndController } from "./promise-controller";
import { delay } from "./promise-utils";
import { abort_startup, beforeOnRuntimeInitialized } from "./startup";
@@ -346,6 +347,7 @@ function download_resource(request: ResourceRequest): LoadingResource {
function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
if (runtimeHelpers.diagnosticTracing)
console.debug(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`);
+ const mark = startMeasure();
const virtualName: string = typeof (asset.virtualPath) === "string"
? asset.virtualPath
@@ -422,6 +424,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
else if (asset.behavior === "resource") {
cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length);
}
+ endMeasure(mark, MeasuredBlock.instantiateAsset, asset.name);
++actual_instantiated_assets_count;
}
diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts
index d65947d498cdb..dde9219c65bb9 100644
--- a/src/mono/wasm/runtime/cwraps.ts
+++ b/src/mono/wasm/runtime/cwraps.ts
@@ -84,7 +84,8 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_getenv", "number", ["string"]],
[true, "mono_wasm_set_main_args", "void", ["number", "number"]],
[false, "mono_wasm_enable_on_demand_gc", "void", ["number"]],
- [false, "mono_profiler_init_aot", "void", ["number"]],
+ [false, "mono_wasm_profiler_init_aot", "void", ["number"]],
+ [false, "mono_wasm_profiler_init_browser", "void", ["number"]],
[false, "mono_wasm_exec_regression", "number", ["number", "string"]],
[false, "mono_wasm_invoke_method_bound", "number", ["number", "number"]],
[true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]],
@@ -93,6 +94,7 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_u52_to_f64", "number", ["number", "number"]],
[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"]],
];
export interface t_Cwraps {
@@ -193,7 +195,8 @@ export interface t_Cwraps {
mono_wasm_getenv(name: string): CharPtr;
mono_wasm_enable_on_demand_gc(enable: number): void;
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
- mono_profiler_init_aot(desc: string): void;
+ mono_wasm_profiler_init_aot(desc: string): void;
+ mono_wasm_profiler_init_browser(desc: string): void;
mono_wasm_exec_regression(verbose_level: number, image: string): number;
mono_wasm_invoke_method_bound(method: MonoMethod, args: JSMarshalerArguments): MonoString;
mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void;
@@ -203,6 +206,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_runtime_run_module_cctor(assembly: MonoAssembly): void;
+ mono_wasm_method_get_name(method: MonoMethod): CharPtr;
}
const wrapped_c_functions: t_Cwraps = {};
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index 20458ac22b886..f4d7563beab8b 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -66,6 +66,7 @@ int32_t monoeg_g_hasenv(const char *variable);
void mono_free (void*);
int32_t mini_parse_debug_option (const char *option);
char *mono_method_get_full_name (MonoMethod *method);
+char *mono_method_full_name (MonoMethod *method, int signature);
static void mono_wasm_init_finalizer_thread (void);
@@ -1399,13 +1400,25 @@ mono_wasm_copy_managed_pointer (PPVOLATILE(MonoObject) destination, PPVOLATILE(M
void mono_profiler_init_aot (const char *desc);
EMSCRIPTEN_KEEPALIVE void
-mono_wasm_load_profiler_aot (const char *desc)
+mono_wasm_profiler_init_aot (const char *desc)
{
mono_profiler_init_aot (desc);
}
#endif
+#ifdef ENABLE_BROWSER_PROFILER
+
+void mono_profiler_init_browser (const char *desc);
+
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_profiler_init_browser (const char *desc)
+{
+ mono_profiler_init_browser (desc);
+}
+
+#endif
+
static void
mono_wasm_init_finalizer_thread (void)
{
@@ -1467,3 +1480,7 @@ EMSCRIPTEN_KEEPALIVE int mono_wasm_f64_to_i52 (int64_t *destination, double valu
*destination = (int64_t)value;
return I52_ERROR_NONE;
}
+
+EMSCRIPTEN_KEEPALIVE const char* mono_wasm_method_get_name (MonoMethod *method) {
+ return mono_method_full_name(method, 0);
+}
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index 9cf4b57ebb724..612330c9df597 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -69,6 +69,10 @@ const linked_functions = [
// mono-threads-wasm.c
"schedule_background_exec",
+ // interp.c
+ "mono_wasm_profiler_enter",
+ "mono_wasm_profiler_leave",
+
// driver.c
"mono_wasm_invoke_js_blazor",
"mono_wasm_trace_logger",
diff --git a/src/mono/wasm/runtime/exports-internal.ts b/src/mono/wasm/runtime/exports-internal.ts
index 01c267ff1ec30..0db08cb407f49 100644
--- a/src/mono/wasm/runtime/exports-internal.ts
+++ b/src/mono/wasm/runtime/exports-internal.ts
@@ -18,7 +18,8 @@ export function export_internal(): any {
// tests
mono_wasm_exit: (exit_code: number) => { Module.printErr("MONO_WASM: early exit " + exit_code); },
mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc,
- mono_profiler_init_aot: cwraps.mono_profiler_init_aot,
+ mono_wasm_profiler_init_aot: cwraps.mono_wasm_profiler_init_aot,
+ mono_wasm_profiler_init_browser: cwraps.mono_wasm_profiler_init_browser,
mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression,
mono_method_resolve,//MarshalTests.cs
mono_intern_string,// MarshalTests.cs
@@ -81,7 +82,8 @@ export function cwraps_internal(internal: any): void {
Object.assign(internal, {
mono_wasm_exit: cwraps.mono_wasm_exit,
mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc,
- mono_profiler_init_aot: cwraps.mono_profiler_init_aot,
+ mono_wasm_profiler_init_aot: cwraps.mono_wasm_profiler_init_aot,
+ mono_wasm_profiler_init_browser: cwraps.mono_wasm_profiler_init_browser,
mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression,
});
}
diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts
index 3c030f2f4f930..2ff03d0c39baf 100644
--- a/src/mono/wasm/runtime/exports-linker.ts
+++ b/src/mono/wasm/runtime/exports-linker.ts
@@ -22,6 +22,7 @@ import { mono_wasm_diagnostic_server_stream_signal_work_available } from "./diag
import { mono_wasm_create_cs_owned_object_ref } from "./net6-legacy/cs-to-js";
import { mono_wasm_typed_array_to_array_ref } from "./net6-legacy/js-to-cs";
import { mono_wasm_trace_logger } from "./logging";
+import { mono_wasm_profiler_leave, mono_wasm_profiler_enter } from "./profiler";
// the methods would be visible to EMCC linker
// --- keep in sync with dotnet.cjs.lib.js ---
@@ -51,13 +52,17 @@ export function export_linker(): any {
// mono-threads-wasm.c
schedule_background_exec,
- // also keep in sync with driver.c
+ // interp.c
+ mono_wasm_profiler_enter,
+ mono_wasm_profiler_leave,
+
+ // driver.c
mono_wasm_invoke_js_blazor,
mono_wasm_trace_logger,
mono_wasm_set_entrypoint_breakpoint,
mono_wasm_event_pipe_early_startup_callback,
- // also keep in sync with corebindings.c
+ // corebindings.c
mono_wasm_invoke_js_with_args_ref,
mono_wasm_get_object_property_ref,
mono_wasm_set_object_property_ref,
@@ -74,7 +79,7 @@ export function export_linker(): any {
mono_wasm_bind_cs_function,
mono_wasm_marshal_promise,
- // also keep in sync with pal_icushim_static.c
+ // pal_icushim_static.c
mono_wasm_load_icu_data,
mono_wasm_get_icudt_name,
diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts
index 581573c6b63c7..293c6731dd433 100644
--- a/src/mono/wasm/runtime/invoke-cs.ts
+++ b/src/mono/wasm/runtime/invoke-cs.ts
@@ -15,9 +15,11 @@ import { Int32Ptr } from "./types/emscripten";
import cwraps from "./cwraps";
import { assembly_load } from "./class-loader";
import { wrap_error_root } from "./invoke-js";
+import { startMeasure, MeasuredBlock, endMeasure } from "./profiler";
export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, signature_hash: number, signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
const fqn_root = mono_wasm_new_external_root(fully_qualified_name), resultRoot = mono_wasm_new_external_root(result_address);
+ const mark = startMeasure();
try {
const version = get_signature_version(signature);
mono_assert(version === 1, () => `Signature version ${version} mismatch.`);
@@ -60,6 +62,7 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef,
const closure: BindingClosure = {
method,
+ fqn: js_fqn,
args_count,
arg_marshalers,
res_converter,
@@ -84,6 +87,7 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef,
(bound_fn)[bound_cs_function_symbol] = true;
_walk_exports_to_set_function(assembly, namespace, classname, methodname, signature_hash, bound_fn);
+ endMeasure(mark, MeasuredBlock.bindCsFunction, js_fqn);
}
catch (ex: any) {
Module.printErr(ex.toString());
@@ -96,8 +100,10 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef,
function bind_fn_0V(closure: BindingClosure) {
const method = closure.method;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_0V() {
+ const mark = startMeasure();
const sp = Module.stackSave();
try {
const args = alloc_stack_frame(2);
@@ -105,6 +111,7 @@ function bind_fn_0V(closure: BindingClosure) {
invoke_method_and_handle_exception(method, args);
} finally {
Module.stackRestore(sp);
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
}
};
}
@@ -112,8 +119,10 @@ function bind_fn_0V(closure: BindingClosure) {
function bind_fn_1V(closure: BindingClosure) {
const method = closure.method;
const marshaler1 = closure.arg_marshalers[0]!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_1V(arg1: any) {
+ const mark = startMeasure();
const sp = Module.stackSave();
try {
const args = alloc_stack_frame(3);
@@ -123,6 +132,7 @@ function bind_fn_1V(closure: BindingClosure) {
invoke_method_and_handle_exception(method, args);
} finally {
Module.stackRestore(sp);
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
}
};
}
@@ -131,8 +141,10 @@ function bind_fn_1R(closure: BindingClosure) {
const method = closure.method;
const marshaler1 = closure.arg_marshalers[0]!;
const res_converter = closure.res_converter!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_1R(arg1: any) {
+ const mark = startMeasure();
const sp = Module.stackSave();
try {
const args = alloc_stack_frame(3);
@@ -145,6 +157,7 @@ function bind_fn_1R(closure: BindingClosure) {
return js_result;
} finally {
Module.stackRestore(sp);
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
}
};
}
@@ -154,8 +167,10 @@ function bind_fn_2R(closure: BindingClosure) {
const marshaler1 = closure.arg_marshalers[0]!;
const marshaler2 = closure.arg_marshalers[1]!;
const res_converter = closure.res_converter!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_2R(arg1: any, arg2: any) {
+ const mark = startMeasure();
const sp = Module.stackSave();
try {
const args = alloc_stack_frame(4);
@@ -169,6 +184,7 @@ function bind_fn_2R(closure: BindingClosure) {
return js_result;
} finally {
Module.stackRestore(sp);
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
}
};
}
@@ -178,8 +194,10 @@ function bind_fn(closure: BindingClosure) {
const arg_marshalers = closure.arg_marshalers;
const res_converter = closure.res_converter;
const method = closure.method;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn(...js_args: any[]) {
+ const mark = startMeasure();
const sp = Module.stackSave();
try {
const args = alloc_stack_frame(2 + args_count);
@@ -200,11 +218,13 @@ function bind_fn(closure: BindingClosure) {
}
} finally {
Module.stackRestore(sp);
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
}
};
}
type BindingClosure = {
+ fqn: string,
args_count: number,
method: MonoMethod,
arg_marshalers: (BoundMarshalerToCs)[],
@@ -254,10 +274,12 @@ export async function mono_wasm_get_assembly_exports(assembly: string): Promise<
mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "The runtime must be initialized.");
const result = exportsByAssembly.get(assembly);
if (!result) {
+ const mark = startMeasure();
const asm = assembly_load(assembly);
if (!asm)
throw new Error("Could not find assembly: " + assembly);
cwraps.mono_wasm_runtime_run_module_cctor(asm);
+ endMeasure(mark, MeasuredBlock.getAssemblyExports, assembly);
}
return exportsByAssembly.get(assembly) || {};
diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts
index b4ef450843e74..07fe9fa5caa4c 100644
--- a/src/mono/wasm/runtime/invoke-js.ts
+++ b/src/mono/wasm/runtime/invoke-js.ts
@@ -12,6 +12,7 @@ import { bind_arg_marshal_to_js } from "./marshal-to-js";
import { mono_wasm_new_external_root } from "./roots";
import { mono_wasm_symbolicate_string } from "./logging";
import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
+import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
const fn_wrapper_by_fn_handle: Function[] = [null];// 0th slot is dummy, we never free bound functions
@@ -24,6 +25,7 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_
mono_assert(version === 1, () => `Signature version ${version} mismatch.`);
const js_function_name = conv_string_root(function_name_root)!;
+ const mark = startMeasure();
const js_module_name = conv_string_root(module_name_root)!;
if (runtimeHelpers.diagnosticTracing) {
console.debug(`MONO_WASM: Binding [JSImport] ${js_function_name} from ${js_module_name}`);
@@ -55,6 +57,7 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_
const closure: BindingClosure = {
fn,
+ fqn: js_module_name + ":" + js_function_name,
args_count,
arg_marshalers,
res_converter,
@@ -82,6 +85,7 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_
const fn_handle = fn_wrapper_by_fn_handle.length;
fn_wrapper_by_fn_handle.push(bound_fn);
setI32(function_js_handle, fn_handle);
+ endMeasure(mark, MeasuredBlock.bindJsFunction, js_function_name);
} catch (ex: any) {
Module.printErr(ex.toString());
wrap_error_root(is_exception, ex, resultRoot);
@@ -93,22 +97,29 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_
function bind_fn_0V(closure: BindingClosure) {
const fn = closure.fn;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_0V(args: JSMarshalerArguments) {
+ const mark = startMeasure();
try {
// call user function
fn();
} catch (ex) {
marshal_exception_to_cs(args, ex);
}
+ finally {
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
+ }
};
}
function bind_fn_1V(closure: BindingClosure) {
const fn = closure.fn;
const marshaler1 = closure.arg_marshalers[0]!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_1V(args: JSMarshalerArguments) {
+ const mark = startMeasure();
try {
const arg1 = marshaler1(args);
// call user function
@@ -116,6 +127,9 @@ function bind_fn_1V(closure: BindingClosure) {
} catch (ex) {
marshal_exception_to_cs(args, ex);
}
+ finally {
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
+ }
};
}
@@ -123,8 +137,10 @@ function bind_fn_1R(closure: BindingClosure) {
const fn = closure.fn;
const marshaler1 = closure.arg_marshalers[0]!;
const res_converter = closure.res_converter!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_1R(args: JSMarshalerArguments) {
+ const mark = startMeasure();
try {
const arg1 = marshaler1(args);
// call user function
@@ -133,6 +149,9 @@ function bind_fn_1R(closure: BindingClosure) {
} catch (ex) {
marshal_exception_to_cs(args, ex);
}
+ finally {
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
+ }
};
}
@@ -141,8 +160,10 @@ function bind_fn_2R(closure: BindingClosure) {
const marshaler1 = closure.arg_marshalers[0]!;
const marshaler2 = closure.arg_marshalers[1]!;
const res_converter = closure.res_converter!;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn_2R(args: JSMarshalerArguments) {
+ const mark = startMeasure();
try {
const arg1 = marshaler1(args);
const arg2 = marshaler2(args);
@@ -152,6 +173,9 @@ function bind_fn_2R(closure: BindingClosure) {
} catch (ex) {
marshal_exception_to_cs(args, ex);
}
+ finally {
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
+ }
};
}
@@ -162,8 +186,10 @@ function bind_fn(closure: BindingClosure) {
const arg_cleanup = closure.arg_cleanup;
const has_cleanup = closure.has_cleanup;
const fn = closure.fn;
+ const fqn = closure.fqn;
(closure) = null;
return function bound_fn(args: JSMarshalerArguments) {
+ const mark = startMeasure();
try {
const js_args = new Array(args_count);
for (let index = 0; index < args_count; index++) {
@@ -190,11 +216,15 @@ function bind_fn(closure: BindingClosure) {
} catch (ex) {
marshal_exception_to_cs(args, ex);
}
+ finally {
+ endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
+ }
};
}
type BindingClosure = {
fn: Function,
+ fqn: string,
args_count: number,
arg_marshalers: (BoundMarshalerToJs)[],
res_converter: BoundMarshalerToCs | undefined,
diff --git a/src/mono/wasm/runtime/profiler.ts b/src/mono/wasm/runtime/profiler.ts
index 45845417e83e5..711a4a9a02ef4 100644
--- a/src/mono/wasm/runtime/profiler.ts
+++ b/src/mono/wasm/runtime/profiler.ts
@@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { Module } from "./imports";
-import { AOTProfilerOptions, CoverageProfilerOptions } from "./types";
+import { AOTProfilerOptions, BrowserProfilerOptions } from "./types";
+import cwraps from "./cwraps";
+import { MonoMethod } from "./types";
// Initialize the AOT profiler with OPTIONS.
// Requires the AOT profiler to be linked into the app.
@@ -20,21 +22,74 @@ export function mono_wasm_init_aot_profiler(options: AOTProfilerOptions): void {
if (!("sendTo" in options))
options.sendTo = "Interop/Runtime::DumpAotProfileData";
const arg = "aot:write-at-method=" + options.writeAt + ",send-to-method=" + options.sendTo;
- Module.ccall("mono_wasm_load_profiler_aot", null, ["string"], [arg]);
+ cwraps.mono_wasm_profiler_init_aot(arg);
}
-// options = { writeAt: "", sendTo: "" }
-// should be in the format ::.
-// writeAt defaults to 'WebAssembly.Runtime::StopProfile'.
-// sendTo defaults to 'WebAssembly.Runtime::DumpCoverageProfileData'.
-// DumpCoverageProfileData stores the data into INTERNAL.coverage_profile_data.
-export function mono_wasm_init_coverage_profiler(options: CoverageProfilerOptions): void {
+export function mono_wasm_init_browser_profiler(options: BrowserProfilerOptions): void {
if (options == null)
options = {};
- if (!("writeAt" in options))
- options.writeAt = "WebAssembly.Runtime::StopProfile";
- if (!("sendTo" in options))
- options.sendTo = "WebAssembly.Runtime::DumpCoverageProfileData";
- const arg = "coverage:write-at-method=" + options.writeAt + ",send-to-method=" + options.sendTo;
- Module.ccall("mono_wasm_load_profiler_coverage", null, ["string"], [arg]);
+ const arg = "browser:";
+ cwraps.mono_wasm_profiler_init_browser(arg);
+}
+
+export const enum MeasuredBlock {
+ emscriptenStartup = "mono.emscriptenStartup",
+ instantiateWasm = "mono.instantiateWasm",
+ preInit = "mono.preInit",
+ preRun = "mono.preRun",
+ onRuntimeInitialized = "mono.onRuntimeInitialized",
+ postRun = "mono.postRun",
+ loadRuntime = "mono.loadRuntime",
+ bindingsInit = "mono.bindingsInit",
+ bindJsFunction = "mono.bindJsFunction:",
+ bindCsFunction = "mono.bindCsFunction:",
+ callJsFunction = "mono.callJsFunction:",
+ callCsFunction = "mono.callCsFunction:",
+ getAssemblyExports = "mono.getAssemblyExports:",
+ instantiateAsset = "mono.instantiateAsset:",
+}
+
+export type TimeStamp = {
+ __brand: "TimeStamp"
+}
+
+export function startMeasure(): TimeStamp {
+ if (performance && typeof performance.measure === "function") {
+ return performance.now() as any;
+ }
+ return undefined as any;
+}
+
+export function endMeasure(start: TimeStamp, block: string, id?: string) {
+ if (start) {
+ if (id) {
+ performance.measure(`${block}${id}`, { start: start as any });
+ } else {
+ performance.measure(block, { start: start as any });
+ }
+ }
+}
+
+const stackFrames: number[] = [];
+export function mono_wasm_profiler_enter(): void {
+ if (performance && typeof performance.measure === "function") {
+ stackFrames.push(performance.now());
+ }
+}
+
+const methodNames: Map = new Map();
+export function mono_wasm_profiler_leave(method: MonoMethod): void {
+ const start = stackFrames.pop();
+ if (performance && performance.measure) {
+ let methodName = methodNames.get(method as any);
+ if (!methodName) {
+ const chars = cwraps.mono_wasm_method_get_name(method);
+ methodName = Module.UTF8ToString(chars);
+ methodNames.set(method as any, methodName);
+ Module._free(chars as any);
+ }
+ performance.measure(methodName, {
+ start
+ });
+ }
}
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index c576927f17725..28767df248770 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -9,7 +9,7 @@ import cwraps, { init_c_exports } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
import { mono_wasm_globalization_init } from "./icu";
import { toBase64StringImpl } from "./base64";
-import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from "./profiler";
+import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler";
import { mono_on_abort, mono_exit } from "./run";
import { initialize_marshalers_to_cs } from "./marshal-to-cs";
import { initialize_marshalers_to_js } from "./marshal-to-js";
@@ -27,6 +27,7 @@ import { BINDING, MONO } from "./net6-legacy/imports";
import { readSymbolMapFile } from "./logging";
import { mono_wasm_init_diagnostics } from "./diagnostics";
import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser";
+import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
let config: MonoConfigInternal = undefined as any;
let configLoaded = false;
@@ -46,6 +47,7 @@ const MONO_PTHREAD_POOL_SIZE = 4;
// we are making emscripten startup async friendly
// emscripten is executing the events without awaiting it and so we need to block progress via PromiseControllers above
export function configure_emscripten_startup(module: DotnetModule, exportedAPI: RuntimeAPI): void {
+ const mark = startMeasure();
// these all could be overridden on DotnetModuleConfig, we are chaing them to async below, as opposed to emscripten
// when user set configSrc or config, we are running our default startup sequence.
const userInstantiateWasm: undefined | ((imports: WebAssembly.Imports, successCallback: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => any) = module.instantiateWasm;
@@ -72,6 +74,8 @@ export function configure_emscripten_startup(module: DotnetModule, exportedAPI:
module.ready = module.ready.then(async () => {
// wait for previous stage
await afterPostRun.promise;
+ // startup end
+ endMeasure(mark, MeasuredBlock.emscriptenStartup);
// - here we resolve the promise returned by createDotnetRuntime export
return exportedAPI;
// - any code after createDotnetRuntime is executed now
@@ -99,8 +103,10 @@ function instantiateWasm(
}
runtimeHelpers.diagnosticTracing = !!config.diagnosticTracing;
+ const mark = startMeasure();
if (userInstantiateWasm) {
const exports = userInstantiateWasm(imports, (instance: WebAssembly.Instance, module: WebAssembly.Module) => {
+ endMeasure(mark, MeasuredBlock.instantiateWasm);
afterInstantiateWasm.promise_control.resolve();
successCallback(instance, module);
});
@@ -113,6 +119,7 @@ function instantiateWasm(
function preInit(userPreInit: (() => void)[]) {
Module.addRunDependency("mono_pre_init");
+ const mark = startMeasure();
try {
mono_wasm_pre_init_essential();
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preInit");
@@ -135,6 +142,7 @@ function preInit(userPreInit: (() => void)[]) {
// - start download assets like DLLs
await mono_wasm_pre_init_full();
}
+ endMeasure(mark, MeasuredBlock.preInit);
} catch (err) {
abort_startup(err, true);
throw err;
@@ -151,12 +159,14 @@ async function preRunAsync(userPreRun: (() => void)[]) {
await afterInstantiateWasm.promise;
await afterPreInit.promise;
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preRunAsync");
- if (MonoWasmThreads) {
- await instantiateWasmPThreadWorkerPool();
- }
+ const mark = startMeasure();
try {
+ if (MonoWasmThreads) {
+ await instantiateWasmPThreadWorkerPool();
+ }
// all user Module.preRun callbacks
userPreRun.map(fn => fn());
+ endMeasure(mark, MeasuredBlock.preRun);
} catch (err) {
_print_error("MONO_WASM: user callback preRun() failed", err);
abort_startup(err, true);
@@ -171,6 +181,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
// wait for previous stage
await afterPreRun.promise;
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: onRuntimeInitialized");
+ const mark = startMeasure();
// signal this stage, this will allow pending assets to allocate memory
beforeOnRuntimeInitialized.promise_control.resolve();
try {
@@ -192,6 +203,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
}
// finish
await mono_wasm_after_user_runtime_initialized();
+ endMeasure(mark, MeasuredBlock.onRuntimeInitialized);
} catch (err) {
_print_error("MONO_WASM: onRuntimeInitializedAsync() failed", err);
abort_startup(err, true);
@@ -206,8 +218,10 @@ async function postRunAsync(userpostRun: (() => void)[]) {
await afterOnRuntimeInitialized.promise;
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: postRunAsync");
try {
+ const mark = startMeasure();
// all user Module.postRun callbacks
userpostRun.map(fn => fn());
+ endMeasure(mark, MeasuredBlock.postRun);
} catch (err) {
_print_error("MONO_WASM: user callback posRun() failed", err);
abort_startup(err, true);
@@ -417,8 +431,9 @@ async function _apply_configuration_from_args() {
if (config.aotProfilerOptions)
mono_wasm_init_aot_profiler(config.aotProfilerOptions);
- if (config.coverageProfilerOptions)
- mono_wasm_init_coverage_profiler(config.coverageProfilerOptions);
+ if (config.browserProfilerOptions)
+ mono_wasm_init_browser_profiler(config.browserProfilerOptions);
+
// for non-Blazor, init diagnostics after environment variables are set
if (MonoWasmThreads) {
await mono_wasm_init_diagnostics();
@@ -432,6 +447,7 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo
}
runtimeHelpers.mono_wasm_load_runtime_done = true;
try {
+ const mark = startMeasure();
if (debugLevel == undefined) {
debugLevel = 0;
if (config && config.debugLevel) {
@@ -439,6 +455,7 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo
}
}
cwraps.mono_wasm_load_runtime(unused || "unused", debugLevel);
+ endMeasure(mark, MeasuredBlock.loadRuntime);
runtimeHelpers.waitForDebugger = config.waitForDebugger;
if (!runtimeHelpers.mono_wasm_bindings_is_ready) bindings_init();
@@ -461,11 +478,13 @@ export function bindings_init(): void {
}
runtimeHelpers.mono_wasm_bindings_is_ready = true;
try {
+ const mark = startMeasure();
init_managed_exports();
init_legacy_exports();
initialize_marshalers_to_js();
initialize_marshalers_to_cs();
runtimeHelpers._i52_error_scratch_buffer = Module._malloc(4);
+ endMeasure(mark, MeasuredBlock.bindingsInit);
} catch (err) {
_print_error("MONO_WASM: Error in bindings_init", err);
throw err;
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index ca914585d1d6e..2355926ed8dee 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -120,7 +120,7 @@ export type MonoConfig = {
export type MonoConfigInternal = MonoConfig & {
runtimeOptions?: string[], // array of runtime options as strings
aotProfilerOptions?: AOTProfilerOptions, // dictionary-style Object. If omitted, aot profiler will not be initialized.
- coverageProfilerOptions?: CoverageProfilerOptions, // dictionary-style Object. If omitted, coverage profiler will not be initialized.
+ browserProfilerOptions?: BrowserProfilerOptions, // dictionary-style Object. If omitted, browser profiler will not be initialized.
waitForDebugger?: number,
appendElementOnExit?: boolean
logExitCode?: boolean
@@ -237,9 +237,7 @@ export type AOTProfilerOptions = {
sendTo?: string // should be in the format ::, default: 'WebAssembly.Runtime::DumpAotProfileData' (DumpAotProfileData stores the data into INTERNAL.aotProfileData.)
}
-export type CoverageProfilerOptions = {
- writeAt?: string, // should be in the format ::, default: 'WebAssembly.Runtime::StopProfile'
- sendTo?: string // should be in the format ::, default: 'WebAssembly.Runtime::DumpCoverageProfileData' (DumpCoverageProfileData stores the data into INTERNAL.coverage_profile_data.)
+export type BrowserProfilerOptions = {
}
// how we extended emscripten Module
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index 0fa4a43fdc8fe..a4e9ea30ca23b 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -240,8 +240,8 @@
$(ArtifactsObjDir)wasm/pinvoke-table.h
$(ArtifactsObjDir)wasm/wasm_m2n_invoke.g.h
- -g -Os -s -DDEBUG=1 -DENABLE_AOT_PROFILER=1
- -Oz
+ -g -Os -s -DDEBUG=1 -DENABLE_AOT_PROFILER=1 -DENABLE_BROWSER_PROFILER=1
+ -Oz -DENABLE_BROWSER_PROFILER=1
$(CMakeConfigurationEmccFlags)
-O2
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 55e93530680f4..113ed9addafc1 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -744,6 +744,15 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
aotAssembly.SetMetadata("AotDataFile", proxyFile.TargetFile);
}
+ if (Profilers?.Length > 0)
+ {
+ foreach (var profiler in Profilers)
+ {
+ processArgs.Add($"\"--profile={profiler}\"");
+ }
+ }
+
+
if (AotProfilePath?.Length > 0)
{
aotArgs.Add("profile-only");