diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets
index 79ad14fb9fca24..cb8b9dbb6f9105 100644
--- a/eng/liveBuilds.targets
+++ b/eng/liveBuilds.targets
@@ -209,6 +209,8 @@
$(LibrariesNativeArtifactsPath)dotnet.native.js;
$(LibrariesNativeArtifactsPath)dotnet.runtime.js;
$(LibrariesNativeArtifactsPath)dotnet.runtime.js.map;
+ $(LibrariesNativeArtifactsPath)dotnet.diagnostics.js;
+ $(LibrariesNativeArtifactsPath)dotnet.diagnostics.js.map;
$(LibrariesNativeArtifactsPath)dotnet.d.ts;
$(LibrariesNativeArtifactsPath)package.json;
$(LibrariesNativeArtifactsPath)dotnet.native.wasm;
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 910aa76c2c28f4..80932ca681b896 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
@@ -224,6 +224,8 @@
+
+
diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj
index e487f605f29a40..b82bfdf21a5ad8 100644
--- a/src/mono/browser/browser.proj
+++ b/src/mono/browser/browser.proj
@@ -340,6 +340,8 @@
{ "identity": "WasmSingleFileBundle", "defaultValueInRuntimePack": "$(WasmSingleFileBundle)" },
{ "identity": "WasmEnableSIMD", "defaultValueInRuntimePack": "$(WasmEnableSIMD)" },
{ "identity": "WasmEnableExceptionHandling", "defaultValueInRuntimePack": "$(WasmEnableExceptionHandling)" },
+ { "identity": "FeaturePerfTracing", "defaultValueInRuntimePack": "$(FeaturePerfTracing)" },
+ { "identity": "WasmProfilers", "defaultValueInRuntimePack": "$(WasmProfilers)" },
{ "identity": "EmccMaximumHeapSize", "defaultValueInRuntimePack": "$(EmccMaximumHeapSize)" }
]
}
@@ -497,6 +499,8 @@
$(NativeBinDir)dotnet.js.map;
$(NativeBinDir)dotnet.runtime.js;
$(NativeBinDir)dotnet.runtime.js.map;
+ $(NativeBinDir)dotnet.diagnostics.js;
+ $(NativeBinDir)dotnet.diagnostics.js.map;
$(NativeBinDir)dotnet.native.js;
$(NativeBinDir)dotnet.d.ts;
$(NativeBinDir)package.json;
diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets
index b5b474c5562e27..bb7c499c91967b 100644
--- a/src/mono/browser/build/BrowserWasmApp.targets
+++ b/src/mono/browser/build/BrowserWasmApp.targets
@@ -3,7 +3,7 @@
true
$(WasmEnableExceptionHandling)
- true
+
false
@@ -90,6 +90,7 @@
+
@@ -101,6 +102,8 @@
Condition="'$(WasmEmitSourceMap)' != 'false'" />
+
@@ -113,6 +116,7 @@
+
boolean), name: string, returnType: string | null, argTypes?: string[], opts?: any];
const threading_cwraps: SigLine[] = WasmEnableThreads ? [
- // MONO.diagnostics
[false, "mono_wasm_init_finalizer_thread", null, []],
[false, "mono_wasm_invoke_jsexport_async_post", "void", ["number", "number", "number"]],
[false, "mono_wasm_invoke_jsexport_sync_send", "void", ["number", "number", "number"]],
@@ -139,7 +138,6 @@ const fn_signatures: SigLine[] = [
];
export interface t_ThreadingCwraps {
- // MONO.diagnostics
mono_wasm_init_finalizer_thread(): void;
mono_wasm_invoke_jsexport_async_post(targetTID: PThreadPtr, method: MonoMethod, args: VoidPtr): void;
mono_wasm_invoke_jsexport_sync_send(targetTID: PThreadPtr, method: MonoMethod, args: VoidPtr): void;
diff --git a/src/mono/browser/runtime/diagnostics.ts b/src/mono/browser/runtime/diagnostics.ts
new file mode 100644
index 00000000000000..4d8675c90e5670
--- /dev/null
+++ b/src/mono/browser/runtime/diagnostics.ts
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { CharPtr, VoidPtr } from "./types/emscripten";
+
+import { diagnosticHelpers } from "./globals";
+
+export function ds_rt_websocket_create (urlPtr :CharPtr):number {
+ return diagnosticHelpers.ds_rt_websocket_create(urlPtr);
+}
+
+export function ds_rt_websocket_send (client_socket :number, buffer:VoidPtr, bytes_to_write:number):number {
+ return diagnosticHelpers.ds_rt_websocket_send(client_socket, buffer, bytes_to_write);
+}
+
+export function ds_rt_websocket_poll (client_socket :number):number {
+ return diagnosticHelpers.ds_rt_websocket_poll(client_socket);
+}
+
+export function ds_rt_websocket_recv (client_socket :number, buffer:VoidPtr, bytes_to_read:number):number {
+ return diagnosticHelpers.ds_rt_websocket_recv(client_socket, buffer, bytes_to_read);
+}
+
+export function ds_rt_websocket_close (client_socket :number):number {
+ return diagnosticHelpers.ds_rt_websocket_close(client_socket);
+}
diff --git a/src/mono/browser/runtime/diagnostics/globals.ts b/src/mono/browser/runtime/diagnostics/globals.ts
new file mode 100644
index 00000000000000..61a6b940dce3da
--- /dev/null
+++ b/src/mono/browser/runtime/diagnostics/globals.ts
@@ -0,0 +1,23 @@
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { DiagnosticHelpers, GlobalObjects, LoaderHelpers, RuntimeHelpers, DotnetModuleInternal } from "../types/internal";
+
+export let _diagnosticModuleLoaded = false; // please keep it in place also as rollup guard
+
+export let diagnosticHelpers: DiagnosticHelpers = null as any;
+export let runtimeHelpers: RuntimeHelpers = null as any;
+export let loaderHelpers: LoaderHelpers = null as any;
+export let Module: DotnetModuleInternal = null as any;
+
+export function setRuntimeGlobalsImpl (globalObjects: GlobalObjects): void {
+ if (_diagnosticModuleLoaded) {
+ throw new Error("Diag module already loaded");
+ }
+ _diagnosticModuleLoaded = true;
+ diagnosticHelpers = globalObjects.diagnosticHelpers;
+ runtimeHelpers = globalObjects.runtimeHelpers;
+ loaderHelpers = globalObjects.loaderHelpers;
+ Module = globalObjects.module;
+}
diff --git a/src/mono/browser/runtime/diagnostics/index.ts b/src/mono/browser/runtime/diagnostics/index.ts
new file mode 100644
index 00000000000000..8ee5274257e5f6
--- /dev/null
+++ b/src/mono/browser/runtime/diagnostics/index.ts
@@ -0,0 +1,32 @@
+// 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 { CharPtr, VoidPtr } from "../types/emscripten";
+
+import { diagnosticHelpers, setRuntimeGlobalsImpl } from "./globals";
+
+/* eslint-disable @typescript-eslint/no-unused-vars */
+export function setRuntimeGlobals (globalObjects: GlobalObjects): void {
+ setRuntimeGlobalsImpl(globalObjects);
+
+ diagnosticHelpers.ds_rt_websocket_create = (urlPtr :CharPtr):number => {
+ throw new Error("Not implemented");
+ };
+
+ diagnosticHelpers.ds_rt_websocket_send = (client_socket :number, buffer:VoidPtr, bytes_to_write:number):number => {
+ throw new Error("Not implemented");
+ };
+
+ diagnosticHelpers.ds_rt_websocket_poll = (client_socket :number):number => {
+ throw new Error("Not implemented");
+ };
+
+ diagnosticHelpers.ds_rt_websocket_recv = (client_socket :number, buffer:VoidPtr, bytes_to_read:number):number => {
+ throw new Error("Not implemented");
+ };
+
+ diagnosticHelpers. ds_rt_websocket_close = (client_socket :number):number => {
+ throw new Error("Not implemented");
+ };
+}
diff --git a/src/mono/browser/runtime/diagnostics/logging.ts b/src/mono/browser/runtime/diagnostics/logging.ts
new file mode 100644
index 00000000000000..727f5a2ff4d71b
--- /dev/null
+++ b/src/mono/browser/runtime/diagnostics/logging.ts
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { loaderHelpers } from "./globals";
+
+/* eslint-disable no-console */
+
+const prefix = "MONO_WASM: ";
+
+export function mono_log_debug (messageFactory: string | (() => string)) {
+ if (loaderHelpers.diagnosticTracing) {
+ const message = (typeof messageFactory === "function"
+ ? messageFactory()
+ : messageFactory);
+ console.debug(prefix + message);
+ }
+}
+
+export function mono_log_info (msg: string, ...data: any) {
+ console.info(prefix + msg, ...data);
+}
+
+export function mono_log_warn (msg: string, ...data: any) {
+ console.warn(prefix + msg, ...data);
+}
+
+export function mono_log_error (msg: string, ...data: any) {
+ if (data && data.length > 0 && data[0] && typeof data[0] === "object") {
+ // don't log silent errors
+ if (data[0].silent) {
+ return;
+ }
+ if (data[0].toString) {
+ console.error(prefix + msg, data[0].toString());
+ return;
+ }
+ }
+ console.error(prefix + msg, ...data);
+}
diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts
index 1a9ba31bb58186..1485010132be76 100644
--- a/src/mono/browser/runtime/dotnet.d.ts
+++ b/src/mono/browser/runtime/dotnet.d.ts
@@ -256,7 +256,7 @@ interface ResourceGroups {
corePdb?: ResourceList;
pdb?: ResourceList;
jsModuleWorker?: ResourceList;
- jsModuleGlobalization?: ResourceList;
+ jsModuleDiagnostics?: ResourceList;
jsModuleNative: ResourceList;
jsModuleRuntime: ResourceList;
wasmSymbols?: ResourceList;
@@ -360,6 +360,10 @@ type SingleAssetBehaviors =
* The javascript module for threads.
*/
| "js-module-threads"
+/**
+ * The javascript module for diagnostic server and client.
+ */
+ | "js-module-diagnostics"
/**
* The javascript module for runtime.
*/
diff --git a/src/mono/browser/runtime/es6/dotnet.es6.lib.js b/src/mono/browser/runtime/es6/dotnet.es6.lib.js
index 2ff1c066d6cd48..306d858b59855f 100644
--- a/src/mono/browser/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/browser/runtime/es6/dotnet.es6.lib.js
@@ -78,7 +78,7 @@ function createWasmImportStubsFrom(collection) {
// we will replace them with the real implementation in replace_linker_placeholders
function injectDependencies() {
createWasmImportStubsFrom(methodIndexByName.mono_wasm_imports);
- createWasmImportStubsFrom(methodIndexByName.mono_wasm_js_globalization_imports);
+ if (FEATURE_PERFTRACING) createWasmImportStubsFrom(methodIndexByName.mono_wasm_diagnostic_imports);
#if USE_PTHREADS
createWasmImportStubsFrom(methodIndexByName.mono_wasm_threads_imports);
diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts
index 29d5eead5f5958..d7175ac9ee634a 100644
--- a/src/mono/browser/runtime/exports-binding.ts
+++ b/src/mono/browser/runtime/exports-binding.ts
@@ -13,7 +13,6 @@ import { mono_wasm_resolve_or_reject_promise } from "./marshal-to-js";
import { mono_wasm_schedule_timer, schedule_background_exec } from "./scheduling";
import { mono_wasm_asm_loaded } from "./startup";
import { mono_log_warn, mono_wasm_console_clear, mono_wasm_trace_logger } from "./logging";
-import { mono_wasm_profiler_record, mono_wasm_profiler_now, ds_rt_websocket_close, ds_rt_websocket_create, ds_rt_websocket_poll, ds_rt_websocket_recv, ds_rt_websocket_send } from "./profiler";
import { mono_wasm_browser_entropy } from "./crypto";
import { mono_wasm_cancel_promise } from "./cancelable-promise";
@@ -24,7 +23,10 @@ import {
} from "./pthreads";
import { mono_wasm_dump_threads } from "./pthreads/ui-thread";
import { mono_wasm_schedule_synchronization_context } from "./pthreads/shared";
-import { mono_wasm_js_globalization_imports } from "./globalization";
+import { mono_wasm_get_locale_info } from "./globalization-locale";
+
+import { mono_wasm_profiler_record, mono_wasm_profiler_now } 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
@@ -48,6 +50,15 @@ export const mono_wasm_threads_imports = !WasmEnableThreads ? [] : [
mono_wasm_warn_about_blocking_wait,
];
+export const mono_wasm_diagnostic_imports = [
+ //event pipe
+ ds_rt_websocket_create,
+ ds_rt_websocket_send,
+ ds_rt_websocket_poll,
+ ds_rt_websocket_recv,
+ ds_rt_websocket_close,
+];
+
export const mono_wasm_imports = [
// mini-wasm.c
mono_wasm_schedule_timer,
@@ -70,6 +81,7 @@ export const mono_wasm_imports = [
mono_interp_flush_jitcall_queue,
mono_wasm_free_method_data,
+ // browser.c
mono_wasm_profiler_now,
mono_wasm_profiler_record,
@@ -88,22 +100,16 @@ export const mono_wasm_imports = [
mono_wasm_invoke_jsimport_ST,
mono_wasm_resolve_or_reject_promise,
mono_wasm_cancel_promise,
-
- //event pipe
- ds_rt_websocket_create,
- ds_rt_websocket_send,
- ds_rt_websocket_poll,
- ds_rt_websocket_recv,
- ds_rt_websocket_close,
+ mono_wasm_get_locale_info,
];
-
+// !!! Keep in sync with exports-linker.ts
const wasmImports: Function[] = [
...mono_wasm_imports,
+ // diagnostic server exports, when enabled
+ ...mono_wasm_diagnostic_imports,
// threading exports, if threading is enabled
...mono_wasm_threads_imports,
- // globalization exports
- ...mono_wasm_js_globalization_imports,
];
export function replace_linker_placeholders (imports: WebAssembly.Imports) {
diff --git a/src/mono/browser/runtime/exports-linker.ts b/src/mono/browser/runtime/exports-linker.ts
index 6eda7b34ae1b82..5c24cc97b0b9f8 100644
--- a/src/mono/browser/runtime/exports-linker.ts
+++ b/src/mono/browser/runtime/exports-linker.ts
@@ -1,15 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { mono_wasm_imports, mono_wasm_threads_imports } from "./exports-binding";
+import { mono_wasm_diagnostic_imports, mono_wasm_imports, mono_wasm_threads_imports } from "./exports-binding";
import gitHash from "consts:gitHash";
-import { mono_wasm_js_globalization_imports } from "./globalization";
export function export_linker_indexes_as_code (): string {
const indexByName: any = {
mono_wasm_imports: {},
mono_wasm_threads_imports: {},
- mono_wasm_js_globalization_imports: {},
+ mono_wasm_diagnostic_imports: {},
};
let idx = 0;
for (const wi of mono_wasm_imports) {
@@ -20,8 +19,8 @@ export function export_linker_indexes_as_code (): string {
indexByName.mono_wasm_threads_imports[wi.name] = idx;
idx++;
}
- for (const wi of mono_wasm_js_globalization_imports) {
- indexByName.mono_wasm_js_globalization_imports[wi.name] = idx;
+ for (const wi of mono_wasm_diagnostic_imports) {
+ indexByName.mono_wasm_diagnostic_imports[wi.name] = idx;
idx++;
}
return `
diff --git a/src/mono/browser/runtime/exports.ts b/src/mono/browser/runtime/exports.ts
index 23d6d30e28800f..3dc0117767161b 100644
--- a/src/mono/browser/runtime/exports.ts
+++ b/src/mono/browser/runtime/exports.ts
@@ -25,6 +25,7 @@ import { forceDisposeProxies } from "./gc-handles";
import { mono_wasm_dump_threads } from "./pthreads";
import { threads_c_functions as tcwraps } from "./cwraps";
+import { utf8ToString } from "./strings";
export let runtimeList: RuntimeList;
@@ -40,7 +41,9 @@ function initializeExports (globalObjects: GlobalObjects): RuntimeAPI {
instantiate_asset,
jiterpreter_dump_stats,
forceDisposeProxies,
-
+ utf8ToString,
+ mono_background_exec: () => tcwraps.mono_background_exec(),
+ mono_wasm_ds_exec: () => tcwraps.mono_wasm_ds_exec(),
};
if (WasmEnableThreads) {
rh.dumpThreads = mono_wasm_dump_threads;
diff --git a/src/mono/browser/runtime/globalization.ts b/src/mono/browser/runtime/globalization.ts
deleted file mode 100644
index c7fb8eeafce9d5..00000000000000
--- a/src/mono/browser/runtime/globalization.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-import { mono_wasm_get_locale_info } from "./globalization-locale";
-
-// JS-based globalization support for WebAssembly
-
-export const mono_wasm_js_globalization_imports = [
- mono_wasm_get_locale_info,
-];
diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts
index e1eeed5ddce1d5..8ba8d54110b406 100644
--- a/src/mono/browser/runtime/globals.ts
+++ b/src/mono/browser/runtime/globals.ts
@@ -8,8 +8,8 @@
import gitHash from "consts:gitHash";
-import { RuntimeAPI } from "./types/index";
-import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController, EmscriptenBuildOptions, GCHandle } from "./types/internal";
+import type { RuntimeAPI } from "./types/index";
+import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController, EmscriptenBuildOptions, GCHandle, DiagnosticHelpers } from "./types/internal";
import { mono_log_error } from "./logging";
// these are our public API (except internal)
@@ -29,6 +29,8 @@ export let ENVIRONMENT_IS_PTHREAD: boolean;
export let exportedRuntimeAPI: RuntimeAPI = null as any;
export let runtimeHelpers: RuntimeHelpers = null as any;
export let loaderHelpers: LoaderHelpers = null as any;
+export let diagnosticHelpers: DiagnosticHelpers = null as any;
+export let globalObjectsRoot: GlobalObjects = null as any;
export let _runtimeModuleLoaded = false; // please keep it in place also as rollup guard
@@ -49,10 +51,12 @@ export function setRuntimeGlobals (globalObjects: GlobalObjects) {
throw new Error("Runtime module already loaded");
}
_runtimeModuleLoaded = true;
+ globalObjectsRoot = globalObjects;
Module = globalObjects.module;
INTERNAL = globalObjects.internal;
runtimeHelpers = globalObjects.runtimeHelpers;
loaderHelpers = globalObjects.loaderHelpers;
+ diagnosticHelpers = globalObjects.diagnosticHelpers;
exportedRuntimeAPI = globalObjects.api;
const rh: Partial = {
diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts
index e749fb42afb783..7ef9caec78204a 100644
--- a/src/mono/browser/runtime/loader/assets.ts
+++ b/src/mono/browser/runtime/loader/assets.ts
@@ -32,6 +32,7 @@ const jsRuntimeModulesAssetTypes: {
"js-module-runtime": true,
"js-module-dotnet": true,
"js-module-native": true,
+ "js-module-diagnostics": true,
};
const jsModulesAssetTypes: {
@@ -119,16 +120,10 @@ function set_single_asset (asset: AssetEntryInternal) {
}
}
-function get_single_asset (behavior: SingleAssetBehaviors): AssetEntryInternal {
+export function try_resolve_single_asset_path (behavior: SingleAssetBehaviors): AssetEntryInternal|undefined {
mono_assert(singleAssetTypes[behavior], `Unknown single asset behavior ${behavior}`);
const asset = singleAssets.get(behavior);
- mono_assert(asset, `Single asset for ${behavior} not found`);
- return asset;
-}
-
-export function resolve_single_asset_path (behavior: SingleAssetBehaviors): AssetEntryInternal {
- const asset = get_single_asset(behavior);
- if (!asset.resolvedUrl) {
+ if (asset && !asset.resolvedUrl) {
asset.resolvedUrl = loaderHelpers.locateFile(asset.name);
if (jsRuntimeModulesAssetTypes[asset.behavior]) {
@@ -147,6 +142,12 @@ export function resolve_single_asset_path (behavior: SingleAssetBehaviors): Asse
return asset;
}
+export function resolve_single_asset_path (behavior: SingleAssetBehaviors): AssetEntryInternal {
+ const asset = try_resolve_single_asset_path(behavior);
+ mono_assert(asset, `Single asset for ${behavior} not found`);
+ return asset;
+}
+
let downloadAssetsStarted = false;
export async function mono_download_assets (): Promise {
if (downloadAssetsStarted) {
@@ -303,6 +304,9 @@ export function prepareAssets () {
convert_single_asset(assetsToLoad, resources.wasmNative, "dotnetwasm");
convert_single_asset(modulesAssets, resources.jsModuleNative, "js-module-native");
convert_single_asset(modulesAssets, resources.jsModuleRuntime, "js-module-runtime");
+ if (resources.jsModuleDiagnostics) {
+ convert_single_asset(modulesAssets, resources.jsModuleDiagnostics, "js-module-diagnostics");
+ }
if (WasmEnableThreads) {
convert_single_asset(modulesAssets, resources.jsModuleWorker, "js-module-threads");
}
@@ -828,6 +832,7 @@ export async function streamingCompileWasm () {
loaderHelpers.wasmCompilePromise.promise_control.reject(err);
}
}
+
export function preloadWorkers () {
if (!WasmEnableThreads) return;
const jsModuleWorker = resolve_single_asset_path("js-module-threads");
diff --git a/src/mono/browser/runtime/loader/config.ts b/src/mono/browser/runtime/loader/config.ts
index 3165672e6257e6..528ce84bdd8dc2 100644
--- a/src/mono/browser/runtime/loader/config.ts
+++ b/src/mono/browser/runtime/loader/config.ts
@@ -71,6 +71,9 @@ function deep_merge_resources (target: ResourceGroups, source: ResourceGroups):
if (providedResources.jsModuleNative !== undefined) {
providedResources.jsModuleNative = { ...(target.jsModuleNative || {}), ...(providedResources.jsModuleNative || {}) };
}
+ if (providedResources.jsModuleDiagnostics !== undefined) {
+ providedResources.jsModuleDiagnostics = { ...(target.jsModuleDiagnostics || {}), ...(providedResources.jsModuleDiagnostics || {}) };
+ }
if (providedResources.jsModuleRuntime !== undefined) {
providedResources.jsModuleRuntime = { ...(target.jsModuleRuntime || {}), ...(providedResources.jsModuleRuntime || {}) };
}
@@ -167,6 +170,9 @@ export function normalizeConfig () {
case "js-module-native":
toMerge.jsModuleNative = resource;
break;
+ case "js-module-diagnostics":
+ toMerge.jsModuleDiagnostics = resource;
+ break;
case "js-module-dotnet":
// don't merge loader
break;
diff --git a/src/mono/browser/runtime/loader/globals.ts b/src/mono/browser/runtime/loader/globals.ts
index 5ef69d0e2e8ced..962d8631cd7c7f 100644
--- a/src/mono/browser/runtime/loader/globals.ts
+++ b/src/mono/browser/runtime/loader/globals.ts
@@ -8,7 +8,7 @@ import { exceptions, simd } from "wasm-feature-detect";
import gitHash from "consts:gitHash";
-import type { DotnetModuleInternal, GlobalObjects, LoaderHelpers, MonoConfigInternal, PThreadWorker, RuntimeHelpers } from "../types/internal";
+import type { DiagnosticHelpers, DotnetModuleInternal, GlobalObjects, LoaderHelpers, MonoConfigInternal, PThreadWorker, RuntimeHelpers } from "../types/internal";
import type { MonoConfig, RuntimeAPI } from "../types";
import { assert_runtime_running, installUnhandledErrorHandler, is_exited, is_runtime_running, mono_exit } from "./exit";
import { assertIsControllablePromise, createPromiseController, getPromiseController } from "./promise-controller";
@@ -33,6 +33,7 @@ export const ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE;
export let runtimeHelpers: RuntimeHelpers = {} as any;
export let loaderHelpers: LoaderHelpers = {} as any;
+export let diagnosticHelpers: DiagnosticHelpers = {} as any;
export let exportedRuntimeAPI: RuntimeAPI = {} as any;
export let INTERNAL: any = {};
export let _loaderModuleLoaded = false; // please keep it in place also as rollup guard
@@ -48,6 +49,7 @@ export const globalObjectsRoot: GlobalObjects = {
module: emscriptenModule,
loaderHelpers,
runtimeHelpers,
+ diagnosticHelpers: diagnosticHelpers,
api: exportedRuntimeAPI,
} as any;
@@ -62,6 +64,7 @@ export function setLoaderGlobals (
_loaderModuleLoaded = true;
runtimeHelpers = globalObjects.runtimeHelpers;
loaderHelpers = globalObjects.loaderHelpers;
+ diagnosticHelpers = globalObjects.diagnosticHelpers;
exportedRuntimeAPI = globalObjects.api;
INTERNAL = globalObjects.internal;
Object.assign(exportedRuntimeAPI, {
diff --git a/src/mono/browser/runtime/loader/run.ts b/src/mono/browser/runtime/loader/run.ts
index dc507d794d3767..b915ddc11ea834 100644
--- a/src/mono/browser/runtime/loader/run.ts
+++ b/src/mono/browser/runtime/loader/run.ts
@@ -4,13 +4,13 @@
import BuildConfiguration from "consts:configuration";
import { type MonoConfig, type DotnetHostBuilder, type DotnetModuleConfig, type RuntimeAPI, type LoadBootResourceCallback } from "../types";
-import type { EmscriptenModuleInternal, RuntimeModuleExportsInternal, NativeModuleExportsInternal } from "../types/internal";
+import type { EmscriptenModuleInternal, RuntimeModuleExportsInternal, NativeModuleExportsInternal, DiagnosticModuleExportsInternal } from "../types/internal";
import { ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, emscriptenModule, exportedRuntimeAPI, globalObjectsRoot, monoConfig, mono_assert } from "./globals";
import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config";
import { installUnhandledErrorHandler, mono_exit, registerEmscriptenExitHandlers } from "./exit";
import { setup_proxy_console, mono_log_info, mono_log_debug } from "./logging";
-import { mono_download_assets, preloadWorkers, prepareAssets, prepareAssetsWorker, resolve_single_asset_path, streamingCompileWasm } from "./assets";
+import { mono_download_assets, preloadWorkers, prepareAssets, prepareAssetsWorker, resolve_single_asset_path, streamingCompileWasm, try_resolve_single_asset_path } from "./assets";
import { detect_features_and_polyfill } from "./polyfills";
import { runtimeHelpers, loaderHelpers } from "./globals";
import { init_globalization } from "./icu";
@@ -434,13 +434,14 @@ export async function createEmscripten (moduleFactory: DotnetModuleConfig | ((ap
let jsModuleRuntimePromise: Promise;
let jsModuleNativePromise: Promise;
+let jsModuleDiagnosticPromise: Promise;
// in the future we can use feature detection to load different flavors
function importModules () {
const jsModuleRuntimeAsset = resolve_single_asset_path("js-module-runtime");
const jsModuleNativeAsset = resolve_single_asset_path("js-module-native");
if (jsModuleRuntimePromise && jsModuleNativePromise) {
- return [jsModuleRuntimePromise, jsModuleNativePromise];
+ return [jsModuleRuntimePromise, jsModuleNativePromise, jsModuleDiagnosticPromise];
}
if (typeof jsModuleRuntimeAsset.moduleExports === "object") {
@@ -456,14 +457,30 @@ function importModules () {
mono_log_debug(() => `Attempting to import '${jsModuleNativeAsset.resolvedUrl}' for ${jsModuleNativeAsset.name}`);
jsModuleNativePromise = import(/*! webpackIgnore: true */jsModuleNativeAsset.resolvedUrl!);
}
- return [jsModuleRuntimePromise, jsModuleNativePromise];
+
+ const jsModuleDiagnosticAsset = try_resolve_single_asset_path("js-module-diagnostics");
+ if (jsModuleDiagnosticAsset) {
+ if (typeof jsModuleDiagnosticAsset.moduleExports === "object") {
+ jsModuleDiagnosticPromise = jsModuleDiagnosticAsset.moduleExports;
+ } else {
+ mono_log_debug(() => `Attempting to import '${jsModuleDiagnosticAsset.resolvedUrl}' for ${jsModuleDiagnosticAsset.name}`);
+ jsModuleDiagnosticPromise = import(/*! webpackIgnore: true */jsModuleDiagnosticAsset.resolvedUrl!);
+ }
+ }
+
+ return [jsModuleRuntimePromise, jsModuleNativePromise, jsModuleDiagnosticPromise];
}
-async function initializeModules (es6Modules: [RuntimeModuleExportsInternal, NativeModuleExportsInternal]) {
+async function initializeModules (es6Modules: [RuntimeModuleExportsInternal, NativeModuleExportsInternal, DiagnosticModuleExportsInternal?]) {
const { initializeExports, initializeReplacements, configureRuntimeStartup, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals, passEmscriptenInternals } = es6Modules[0];
const { default: emscriptenFactory } = es6Modules[1];
+ const diagnosticModule = es6Modules[2];
setRuntimeGlobals(globalObjectsRoot);
initializeExports(globalObjectsRoot);
+ if (diagnosticModule) {
+ diagnosticModule.setRuntimeGlobals(globalObjectsRoot);
+ }
+
await configureRuntimeStartup(emscriptenModule);
loaderHelpers.runtimeModuleLoaded.promise_control.resolve();
diff --git a/src/mono/browser/runtime/rollup.config.js b/src/mono/browser/runtime/rollup.config.js
index c1e3874c052766..8147a3dc58779d 100644
--- a/src/mono/browser/runtime/rollup.config.js
+++ b/src/mono/browser/runtime/rollup.config.js
@@ -95,6 +95,11 @@ const checkNoRuntime =
pattern: /_runtimeModuleLoaded/gm,
failure: "module should not contain runtimeModuleLoaded member. This is probably duplicated code in the output caused by a dependency on the runtime module."
};
+const checkNoDiagnostics =
+{
+ pattern: /_diagnosticModuleLoaded/gm,
+ failure: "module should not contain _diagnosticModuleLoaded member. This is probably duplicated code in the output caused by a dependency on the runtime module."
+};
let gitHash;
@@ -174,7 +179,7 @@ const loaderConfig = {
}
],
external: externalDependencies,
- plugins: [nodeResolve(), regexReplace(inlineAssert), regexCheck([checkAssert, checkNoRuntime]), ...outputCodePlugins],
+ plugins: [nodeResolve(), regexReplace(inlineAssert), regexCheck([checkAssert, checkNoRuntime, checkNoDiagnostics]), ...outputCodePlugins],
onwarn: onwarn
};
const runtimeConfig = {
@@ -191,7 +196,24 @@ const runtimeConfig = {
}
],
external: externalDependencies,
- plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoLoader]), ...outputCodePlugins],
+ plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoLoader, checkNoDiagnostics]), ...outputCodePlugins],
+ onwarn: onwarn
+};
+const diagConfig = {
+ treeshake: !isDebug,
+ input: "diagnostics/index.ts",
+ output: [
+ {
+ format: "es",
+ file: nativeBinDir + "/dotnet.diagnostics.js",
+ banner,
+ plugins,
+ sourcemap: true,
+ sourcemapPathTransform,
+ }
+ ],
+ external: externalDependencies,
+ plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoLoader, checkNoRuntime]), ...outputCodePlugins],
onwarn: onwarn
};
const wasmImportsConfig = {
@@ -241,6 +263,7 @@ if (isDebug) {
const allConfigs = [
loaderConfig,
runtimeConfig,
+ diagConfig,
wasmImportsConfig,
typesConfig,
]
diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts
index eb6c12a1b3e2e5..46a39a816d946a 100644
--- a/src/mono/browser/runtime/types/index.ts
+++ b/src/mono/browser/runtime/types/index.ts
@@ -211,7 +211,7 @@ export interface ResourceGroups {
pdb?: ResourceList;
jsModuleWorker?: ResourceList;
- jsModuleGlobalization?: ResourceList;
+ jsModuleDiagnostics?: ResourceList;
jsModuleNative: ResourceList;
jsModuleRuntime: ResourceList;
wasmSymbols?: ResourceList;
@@ -318,6 +318,10 @@ export type SingleAssetBehaviors =
* The javascript module for threads.
*/
| "js-module-threads"
+ /**
+ * The javascript module for diagnostic server and client.
+ */
+ | "js-module-diagnostics"
/**
* The javascript module for runtime.
*/
diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts
index 8dd0e98d08e0a8..61e82dc1eceb66 100644
--- a/src/mono/browser/runtime/types/internal.ts
+++ b/src/mono/browser/runtime/types/internal.ts
@@ -243,14 +243,17 @@ export type RuntimeHelpers = {
forceDisposeProxies: (disposeMethods: boolean, verbose: boolean) => void,
dumpThreads: () => void,
mono_wasm_print_thread_dump: () => void,
+ utf8ToString: (ptr: CharPtr) => string,
+ mono_background_exec: () =>void;
+ mono_wasm_ds_exec: () =>void;
+}
- stringToUTF16: (dstPtr: number, endPtr: number, text: string) => void,
- stringToUTF16Ptr: (str: string) => VoidPtr,
- utf16ToString: (startPtr: number, endPtr: number) => string,
- utf16ToStringLoop: (startPtr: number, endPtr: number) => string,
- localHeapViewU16: () => Uint16Array,
- setU16_local: (heap: Uint16Array, ptr: number, value: number) => void,
- setI32: (offset: MemOffset, value: number) => void,
+export type DiagnosticHelpers = {
+ ds_rt_websocket_create:(urlPtr :CharPtr)=>number,
+ ds_rt_websocket_send:(client_socket :number, buffer:VoidPtr, bytes_to_write:number)=>number,
+ ds_rt_websocket_poll:(client_socket :number)=>number,
+ ds_rt_websocket_recv:(client_socket :number, buffer:VoidPtr, bytes_to_read:number)=>number,
+ ds_rt_websocket_close:(client_socket :number)=>number,
}
export type AOTProfilerOptions = {
@@ -310,6 +313,7 @@ export type GlobalObjects = {
module: DotnetModuleInternal,
loaderHelpers: LoaderHelpers,
runtimeHelpers: RuntimeHelpers,
+ diagnosticHelpers: DiagnosticHelpers,
api: RuntimeAPI,
};
export type EmscriptenReplacements = {
@@ -505,6 +509,10 @@ export type RuntimeModuleExportsInternal = {
passEmscriptenInternals: passEmscriptenInternalsType,
}
+export type DiagnosticModuleExportsInternal = {
+ setRuntimeGlobals: setGlobalObjectsType,
+}
+
export type NativeModuleExportsInternal = {
default: (unificator: Function) => Promise
}
diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index 5e4f39455f76ad..17e6e261f7e93b 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -242,6 +242,7 @@ Copyright (c) .NET Foundation. All rights reserved.
CopySymbols="$(_WasmCopyOutputSymbolsToOutputDirectory)"
OutputPath="$(OutputPath)"
EnableThreads="$(_WasmEnableThreads)"
+ FeaturePerfTracing="$(FeaturePerfTracing)"
EmitSourceMap="$(_WasmEmitSourceMapBuild)"
FingerprintAssets="$(_WasmFingerprintAssets)"
FingerprintDotnetJs="$(_WasmFingerprintDotnetJs)"
@@ -479,6 +480,7 @@ Copyright (c) .NET Foundation. All rights reserved.
CopySymbols="$(CopyOutputSymbolsToPublishDirectory)"
ExistingAssets="@(_WasmPublishPrefilteredAssets)"
EnableThreads="$(_WasmEnableThreads)"
+ FeaturePerfTracing="$(FeaturePerfTracing)"
EmitSourceMap="$(_WasmEmitSourceMapPublish)"
IsWebCilEnabled="$(_WasmEnableWebcil)"
FingerprintAssets="$(_WasmFingerprintAssets)"
diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets
index 207f85efedf1e2..30db0e4f88953c 100644
--- a/src/mono/sample/wasm/Directory.Build.targets
+++ b/src/mono/sample/wasm/Directory.Build.targets
@@ -18,6 +18,7 @@
<_ServeMimeTypes>$(_ServeMimeTypes) --mime .cjs=text/javascript
<_ServeMimeTypes>$(_ServeMimeTypes) --mime .js=text/javascript
<_ServeMimeTypes>$(_ServeMimeTypes) --mime .webcil=application/octet-stream
+ true
+
+
+
diff --git a/src/mono/wasi/wasi.proj b/src/mono/wasi/wasi.proj
index 1cd944f822832d..47a270575245e5 100644
--- a/src/mono/wasi/wasi.proj
+++ b/src/mono/wasi/wasi.proj
@@ -164,6 +164,8 @@
{ "identity": "InvariantGlobalization", "defaultValueInRuntimePack": "$(InvariantGlobalization)" },
{ "identity": "WasmNativeStrip", "defaultValueInRuntimePack": "$(WasmNativeStrip)" },
{ "identity": "WasmSingleFileBundle", "defaultValueInRuntimePack": "$(WasmSingleFileBundle)" },
+ { "identity": "FeaturePerfTracing", "defaultValueInRuntimePack": "$(FeaturePerfTracing)" },
+ { "identity": "WasmProfilers", "defaultValueInRuntimePack": "$(WasmProfilers)" },
{ "identity": "WasmEnableSIMD", "defaultValueInRuntimePack": "$(WasmEnableSIMD)" }
]
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/BuildOptions.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/BuildOptions.cs
index d573405986a9cd..76a9053d9de493 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/BuildOptions.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/BuildOptions.cs
@@ -25,7 +25,8 @@ public BuildOptions(
IDictionary? ExtraBuildEnvironmentVariables = null,
string BootConfigFileName = "blazor.boot.json",
string NonDefaultFrameworkDir = "",
- string ExtraMSBuildArgs = ""
+ string ExtraMSBuildArgs = "",
+ bool FeaturePerfTracing = false
) : base(
IsPublish,
AOT,
@@ -42,7 +43,8 @@ public BuildOptions(
ExtraBuildEnvironmentVariables,
BootConfigFileName,
NonDefaultFrameworkDir,
- ExtraMSBuildArgs
+ ExtraMSBuildArgs,
+ FeaturePerfTracing
)
{
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/MSBuildOptions.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/MSBuildOptions.cs
index 24ae96b0745816..13506f5eee71e9 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/MSBuildOptions.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/MSBuildOptions.cs
@@ -24,5 +24,6 @@ public abstract record MSBuildOptions
IDictionary? ExtraBuildEnvironmentVariables = null,
string BootConfigFileName = "blazor.boot.json",
string NonDefaultFrameworkDir = "",
- string ExtraMSBuildArgs = ""
+ string ExtraMSBuildArgs = "",
+ bool FeaturePerfTracing = false
);
diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/PublishOptions.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/PublishOptions.cs
index 4eda155ad5d6f4..8d8d543d1bcc52 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/PublishOptions.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/PublishOptions.cs
@@ -30,7 +30,8 @@ public PublishOptions(
string NonDefaultFrameworkDir = "",
string ExtraMSBuildArgs = "",
bool BuildOnlyAfterPublish = true,
- bool ExpectRelinkDirWhenPublishing = false
+ bool ExpectRelinkDirWhenPublishing = false,
+ bool FeaturePerfTracing = false
) : base(
IsPublish,
AOT,
@@ -47,7 +48,8 @@ public PublishOptions(
ExtraBuildEnvironmentVariables,
BootConfigFileName,
NonDefaultFrameworkDir,
- ExtraMSBuildArgs
+ ExtraMSBuildArgs,
+ FeaturePerfTracing
)
{
this.IsPublish = IsPublish;
diff --git a/src/mono/wasm/Wasm.Build.Tests/DiagnosticsTests.cs b/src/mono/wasm/Wasm.Build.Tests/DiagnosticsTests.cs
new file mode 100644
index 00000000000000..a56b88c9e86732
--- /dev/null
+++ b/src/mono/wasm/Wasm.Build.Tests/DiagnosticsTests.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Text.RegularExpressions;
+using Xunit.Abstractions;
+using Xunit;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public class DiagnosticsTests : WasmTemplateTestsBase
+{
+ public DiagnosticsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [Fact]
+ public async Task RunSimpleAppWithProfiler()
+ {
+ Configuration config = Configuration.Release;
+ ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ProfilerTest");
+ // are are linking all 3 profilers, but below we only initialize log profiler and test it
+ string extraArgs = $"-p:WasmProfilers=\"aot+browser+log\"";
+ BuildProject(info, config, new BuildOptions(ExtraMSBuildArgs: extraArgs, AssertAppBundle: false, FeaturePerfTracing: true), isNativeBuild: true);
+
+ var result = await RunForBuildWithDotnetRun(new BrowserRunOptions(Configuration: config, TestScenario: "ProfilerTest"));
+ Regex regex = new Regex(@"Profile data of size (\d+) bytes");
+ var match = result.TestOutput
+ .Select(line => regex.Match(line))
+ .FirstOrDefault(m => m.Success);
+ Assert.True(match != null, $"TestOutput did not contain log matching {regex}");
+ if (!int.TryParse(match.Groups[1].Value, out int fileSize))
+ {
+ Assert.Fail($"Failed to parse profile size from {match.Groups[1].Value} to int");
+ }
+ Assert.True(fileSize >= 10 * 1024, $"Profile file size is less than 10KB. Actual size: {fileSize} bytes.");
+ }
+}
diff --git a/src/mono/wasm/Wasm.Build.Tests/MemoryTests.cs b/src/mono/wasm/Wasm.Build.Tests/MemoryTests.cs
index 39d7f5be61c5d8..1b0697d7704483 100644
--- a/src/mono/wasm/Wasm.Build.Tests/MemoryTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/MemoryTests.cs
@@ -45,26 +45,4 @@ await RunForBuildWithDotnetRun(new BrowserRunOptions(
));
}
}
-
- [Fact]
- public async Task RunSimpleAppWithProfiler()
- {
- Configuration config = Configuration.Release;
- ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ProfilerTest");
- // are are linking all 3 profilers, but below we only initialize log profiler and test it
- string extraArgs = $"-p:WasmProfilers=\"aot+browser+log\" -p:WasmBuildNative=true";
- BuildProject(info, config, new BuildOptions(ExtraMSBuildArgs: extraArgs, AssertAppBundle: false), isNativeBuild: true);
-
- var result = await RunForBuildWithDotnetRun(new BrowserRunOptions(Configuration: config, TestScenario: "ProfilerTest"));
- Regex regex = new Regex(@"Profile data of size (\d+) bytes");
- var match = result.TestOutput
- .Select(line => regex.Match(line))
- .FirstOrDefault(m => m.Success);
- Assert.True(match != null, $"TestOuptup did not contain log matching {regex}");
- if (!int.TryParse(match.Groups[1].Value, out int fileSize))
- {
- Assert.Fail($"Failed to parse profile size from {match.Groups[1].Value} to int");
- }
- Assert.True(fileSize >= 10 * 1024, $"Profile file size is less than 10KB. Actual size: {fileSize} bytes.");
- }
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
index 56cd08e08eef7f..979823780f2349 100644
--- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
@@ -83,8 +83,10 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles(Asse
EnsureProjectDirIsSet();
return FindAndAssertDotnetFiles(binFrameworkDir: assertOptions.BinFrameworkDir,
expectFingerprintOnDotnetJs: IsFingerprintingOnDotnetJsEnabled,
+ assertOptions,
superSet: GetAllKnownDotnetFilesToFingerprintMap(assertOptions),
- expected: GetDotNetFilesExpectedSet(assertOptions));
+ expected: GetDotNetFilesExpectedSet(assertOptions)
+ );
}
protected abstract IReadOnlyDictionary GetAllKnownDotnetFilesToFingerprintMap(AssertBundleOptions assertOptions);
@@ -93,15 +95,15 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles(Asse
public IReadOnlyDictionary FindAndAssertDotnetFiles(
string binFrameworkDir,
bool expectFingerprintOnDotnetJs,
+ AssertBundleOptions assertOptions,
IReadOnlyDictionary superSet,
- IReadOnlySet? expected)
+ IReadOnlySet expected)
{
EnsureProjectDirIsSet();
var actual = new SortedDictionary();
if (!Directory.Exists(binFrameworkDir))
throw new XunitException($"Could not find bundle directory {binFrameworkDir}");
-
IList dotnetFiles = Directory.EnumerateFiles(binFrameworkDir,
"dotnet.*",
SearchOption.TopDirectoryOnly)
@@ -155,15 +157,19 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles(
{
throw new XunitException($"Found unknown files in {binFrameworkDir}:{Environment.NewLine} " +
$"{string.Join($"{Environment.NewLine} ", dotnetFiles.Select(f => Path.GetRelativePath(binFrameworkDir, f)))}{Environment.NewLine}" +
- $"Add these to {nameof(GetAllKnownDotnetFilesToFingerprintMap)} method");
+ $"Add these to {nameof(GetAllKnownDotnetFilesToFingerprintMap)} method{Environment.NewLine}" +
+ $"Expected {string.Join($"{Environment.NewLine} ", expected)}{Environment.NewLine}" +
+ $"Options {assertOptions} {Environment.NewLine}"
+ );
}
if (expected is not null)
- AssertDotNetFilesSet(expected, superSet, actual, expectFingerprintOnDotnetJs, binFrameworkDir);
+ AssertDotNetFilesSet(assertOptions, expected, superSet, actual, expectFingerprintOnDotnetJs, binFrameworkDir);
return actual;
}
private void AssertDotNetFilesSet(
+ AssertBundleOptions assertOptions,
IReadOnlySet expected,
IReadOnlyDictionary superSet,
IReadOnlyDictionary actualReadOnly,
@@ -177,7 +183,7 @@ private void AssertDotNetFilesSet(
{
bool expectFingerprint = superSet[expectedFilename];
- Assert.True(actual.ContainsKey(expectedFilename), $"Could not find {expectedFilename} in bundle directory: {bundleDir}. Actual files on disk: {string.Join(", ", actual.Keys)}");
+ Assert.True(actual.ContainsKey(expectedFilename), $"Could not find {expectedFilename} in bundle directory: {bundleDir}. Actual files on disk: {string.Join(", ", actual.Keys)} Options {assertOptions}");
// Check that the version and hash are present or not present as expected
if (ShouldCheckFingerprint(expectedFilename: expectedFilename,
@@ -185,12 +191,12 @@ private void AssertDotNetFilesSet(
expectFingerprintForThisFile: expectFingerprint))
{
if (string.IsNullOrEmpty(actual[expectedFilename].Hash))
- throw new XunitException($"Expected hash in filename: {actual[expectedFilename].ActualPath}");
+ throw new XunitException($"Expected hash in filename: {actual[expectedFilename].ActualPath} Options {assertOptions}");
}
else
{
if (!string.IsNullOrEmpty(actual[expectedFilename].Hash))
- throw new XunitException($"Expected no hash in filename: {actual[expectedFilename].ActualPath}");
+ throw new XunitException($"Expected no hash in filename: {actual[expectedFilename].ActualPath} Options {assertOptions}");
}
actual.Remove(expectedFilename);
}
@@ -198,7 +204,7 @@ private void AssertDotNetFilesSet(
if (actual.Any())
{
var actualFileNames = actual.Values.Select(x => x.ActualPath).Order();
- throw new XunitException($"Found unexpected files: {string.Join(", ", actualFileNames)}");
+ throw new XunitException($"Found unexpected files: {string.Join(", ", actualFileNames)} Options {assertOptions}");
}
}
@@ -508,7 +514,7 @@ public BootJsonData AssertBootJson(AssertBundleOptions options)
.Union(bootJson.resources.wasmNative.Keys)
.Union(bootJson.resources.jsModuleRuntime.Keys)
.Union(bootJson.resources.jsModuleWorker?.Keys ?? Enumerable.Empty())
- .Union(bootJson.resources.jsModuleGlobalization?.Keys ?? Enumerable.Empty())
+ .Union(bootJson.resources.jsModuleDiagnostics?.Keys ?? Enumerable.Empty())
.Union(bootJson.resources.wasmSymbols?.Keys ?? Enumerable.Empty())
.ToArray();
diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
index 89b99ca922fe10..184d094ea82dec 100644
--- a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
@@ -35,6 +35,8 @@ protected override IReadOnlyDictionary GetAllKnownDotnetFilesToFin
{ "dotnet.native.worker.mjs", true },
{ "dotnet.runtime.js", true },
{ "dotnet.runtime.js.map", false },
+ { "dotnet.diagnostics.js", true },
+ { "dotnet.diagnostics.js.map", false },
};
protected override IReadOnlySet GetDotNetFilesExpectedSet(AssertBundleOptions assertOptions)
@@ -60,6 +62,13 @@ protected override IReadOnlySet GetDotNetFilesExpectedSet(AssertBundleOp
if (assertOptions.AssertSymbolsFile && assertOptions.ExpectSymbolsFile)
res.Add("dotnet.native.js.symbols");
+ if (assertOptions.BuildOptions.FeaturePerfTracing)
+ {
+ res.Add("dotnet.diagnostics.js");
+ if (!assertOptions.BuildOptions.IsPublish)
+ res.Add("dotnet.diagnostics.js.map");
+ }
+
return res;
}
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
index ecbc3e9c6b631b..b4188444d901dd 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
@@ -25,6 +25,11 @@ public class AssetsComputingHelper
"dotnet.runtime"
};
+ private static readonly string[] dotnetJsDiagNames = new[]
+ {
+ "dotnet.diagnostics",
+ };
+
private static readonly string[] icuShardsFromRuntimePack = new[]
{
"icudt_EFIGS",
@@ -40,6 +45,7 @@ public static bool ShouldFilterCandidate(
bool copySymbols,
string customIcuCandidateFilename,
bool enableThreads,
+ bool featurePerfTracing,
bool emitSourceMap,
out string reason)
{
@@ -66,9 +72,15 @@ public static bool ShouldFilterCandidate(
".dat" when IsDefaultIcuMode() && !(icuShardsFromRuntimePack.Any(f => f == fileName)) => "automatic icu shard selection, based on application culture, is enabled",
".json" when fromMonoPackage && (fileName == "wasm-props" || fileName == "package") => $"{fileName}{extension} is not used by Blazor",
".ts" when fromMonoPackage && fileName == "dotnet.d" => "dotnet type definition is not used by Blazor",
- ".map" when !emitSourceMap && fromMonoPackage && (fileName == "dotnet.js" || fileName == "dotnet.runtime.js") => "source map file is not published",
+ ".map" when emitSourceMap && fromMonoPackage && (fileName == "dotnet.js" || fileName == "dotnet.runtime.js") => null,
+ ".map" when emitSourceMap && fromMonoPackage && featurePerfTracing && fileName == "dotnet.diagnostics.js" => null,
+ ".map" when emitSourceMap && fromMonoPackage && !featurePerfTracing && fileName == "dotnet.diagnostics.js" => "perf tracing is not enabled",
+ ".map" when !emitSourceMap && fromMonoPackage => "source map file is not published",
".ts" when fromMonoPackage && fileName == "dotnet-legacy.d" => "dotnet type definition is not used by Blazor",
- ".js" when assetType == "native" && !dotnetJsSingleThreadNames.Contains(fileName) => $"{fileName}{extension} is not used by Blazor",
+ ".js" when assetType == "native" && dotnetJsSingleThreadNames.Contains(fileName) => null,
+ ".js" when assetType == "native" && featurePerfTracing && dotnetJsDiagNames.Contains(fileName) => null,
+ ".js" when assetType == "native" && !featurePerfTracing && dotnetJsDiagNames.Contains(fileName) => "perf tracing is not enabled",
+ ".js" when assetType == "native" => $"{fileName}{extension} is not used by Blazor",
".mjs" when assetType == "native" && !(enableThreads && fileName == "dotnet.native.worker") => $"{fileName}{extension} is not used by Blazor",
".pdb" when !copySymbols => "copying symbols is disabled",
".symbols" when fromMonoPackage => "extension .symbols is not required.",
@@ -115,6 +127,7 @@ public static string GetCandidateRelativePath(ITaskItem candidate, bool fingerpr
{
("dotnet", ".js") => string.Concat(fileName, fingerprintDotNetJs ? requiredFingerprint : optionalFingerprint, extension),
("dotnet.runtime", ".js") => string.Concat(fileName, requiredFingerprint, extension),
+ ("dotnet.diagnostics", ".js") => string.Concat(fileName, requiredFingerprint, extension),
("dotnet.native", ".js") => string.Concat(fileName, requiredFingerprint, extension),
("dotnet.native.worker", ".mjs") => string.Concat(fileName, requiredFingerprint, extension),
_ => string.Concat(fileName, extension)
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
index 7c6c0836f4a2ce..2077fb40d6e0fe 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
@@ -54,7 +54,7 @@ static void AddDictionary(StringBuilder sb, Dictionary? res)
AddDictionary(sb, bootConfig.resources.coreAssembly);
AddDictionary(sb, bootConfig.resources.jsModuleWorker);
- AddDictionary(sb, bootConfig.resources.jsModuleGlobalization);
+ AddDictionary(sb, bootConfig.resources.jsModuleDiagnostics);
AddDictionary(sb, bootConfig.resources.jsModuleNative);
AddDictionary(sb, bootConfig.resources.jsModuleRuntime);
AddDictionary(sb, bootConfig.resources.wasmNative);
@@ -89,8 +89,8 @@ static void AddDictionary(StringBuilder sb, Dictionary? res)
string resourceExtension = Path.GetExtension(resourceName);
if (resourceName.StartsWith("dotnet.native.worker", StringComparison.OrdinalIgnoreCase) && string.Equals(resourceExtension, ".mjs", StringComparison.OrdinalIgnoreCase))
return bootConfig.resources.jsModuleWorker ??= new();
- if (resourceName.StartsWith("dotnet.globalization", StringComparison.OrdinalIgnoreCase) && string.Equals(resourceExtension, ".js", StringComparison.OrdinalIgnoreCase))
- return bootConfig.resources.jsModuleGlobalization ??= new();
+ else if (resourceName.StartsWith("dotnet.diagnostics", StringComparison.OrdinalIgnoreCase) && string.Equals(resourceExtension, ".js", StringComparison.OrdinalIgnoreCase))
+ return bootConfig.resources.jsModuleDiagnostics ??= new();
else if (resourceName.StartsWith("dotnet.native", StringComparison.OrdinalIgnoreCase) && string.Equals(resourceExtension, ".js", StringComparison.OrdinalIgnoreCase))
return bootConfig.resources.jsModuleNative ??= new();
else if (resourceName.StartsWith("dotnet.runtime", StringComparison.OrdinalIgnoreCase) && string.Equals(resourceExtension, ".js", StringComparison.OrdinalIgnoreCase))
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
index 32e09c9826c109..4d3c57ff4580ff 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
@@ -141,7 +141,7 @@ public class ResourcesData
public ResourceHashesByNameDictionary jsModuleWorker { get; set; }
[DataMember(EmitDefaultValue = false)]
- public ResourceHashesByNameDictionary jsModuleGlobalization { get; set; }
+ public ResourceHashesByNameDictionary jsModuleDiagnostics { get; set; }
[DataMember(EmitDefaultValue = false)]
public ResourceHashesByNameDictionary jsModuleNative { get; set; }
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
index a9e9b91cb4e9e1..7d64709ad58f2b 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
@@ -48,6 +48,8 @@ public class ComputeWasmBuildAssets : Task
public bool EnableThreads { get; set; }
+ public bool FeaturePerfTracing { get; set; }
+
public bool EmitSourceMap { get; set; }
public bool FingerprintAssets { get; set; }
@@ -88,7 +90,7 @@ public override bool Execute()
for (int i = 0; i < Candidates.Length; i++)
{
var candidate = Candidates[i];
- if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
+ if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, FeaturePerfTracing, EmitSourceMap, out var reason))
{
Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
filesToRemove.Add(candidate);
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
index 613301b7a722de..384e710b154a15 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
@@ -54,6 +54,8 @@ public class ComputeWasmPublishAssets : Task
public bool EnableThreads { get; set; }
+ public bool FeaturePerfTracing { get; set; }
+
public bool EmitSourceMap { get; set; }
public bool IsWebCilEnabled { get; set; }
@@ -220,6 +222,8 @@ private void ProcessNativeAssets(
baseName = "dotnet.native";
else if (baseName.StartsWith("dotnet.runtime"))
baseName = "dotnet.runtime";
+ else if (baseName.StartsWith("dotnet.diagnostics"))
+ baseName = "dotnet.diagnostics";
else if (baseName.StartsWith("dotnet"))
baseName = "dotnet";
@@ -659,7 +663,7 @@ private void GroupResolvedFilesToPublish(
foreach (var candidate in resolvedFilesToPublish)
{
#pragma warning disable CA1864 // Prefer the 'IDictionary.TryAdd(TKey, TValue)' method. Dictionary.TryAdd() not available in .Net framework.
- if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
+ if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, FeaturePerfTracing, EmitSourceMap, out var reason))
{
Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
if (!resolvedFilesToPublishToRemove.ContainsKey(candidate.ItemSpec))
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index 2befd468f50d6d..b6eaa0e170141c 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -169,7 +169,7 @@ protected override bool ExecuteInternal()
if (!IncludeThreadsWorker && name == "dotnet.native.worker.mjs")
continue;
- if (name == "dotnet.runtime.js.map" || name == "dotnet.js.map")
+ if (name == "dotnet.runtime.js.map" || name == "dotnet.js.map" || name == "dotnet.diagnostics.js.map")
{
Log.LogMessage(MessageImportance.Low, $"Skipping {item.ItemSpec} from boot config");
continue;