diff options
author | Aleksey Kliger (λgeek) <aleksey@lambdageek.org> | 2022-08-08 21:29:15 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-08 21:29:15 +0300 |
commit | 89db9c050b1baa7a8be686a128b1b8a93d084a0a (patch) | |
tree | ac8ea8cf1702d1367e012948febfdd3baba86f35 /src/mono | |
parent | a688d43a1035c9d60c2f0050883b31774bbb66c9 (diff) |
[wasm-ep] Use DOTNET_DiagnosticPorts to configure Diagnostic Server (#73370)
* [wasm-ep] Use DOTNET_DiagnosticPorts to configure Diagnostic Server
Parse it the same way that the C code does:
```
<uri>[,<connect|listen>][,<suspend|nosuspend>]
```
- uri should be a websocket uri
- listen is not supported as it doesn't make sense with a WebSocket
- connect is the default if omitted
- suspend is the default if omitted
---
Additionally, move `mono_wasm_diagnostics_init` to later in the
startup flow. This gives Blazor a chance to set
DOTNET_DiagnosticPorts from their `onRuntimeInitialized` callback.
Fixes https://github.com/dotnet/runtime/issues/73011
* Initialize diagnostic server in different places for Blazor and non-Blazor
It has to be after environment variables are set, but before
mono_wasm_load_runtime is called.
There is no good place that's common to both startup paths. Try it on
both. Use a flag to make diagnostics initialization run at most
once
* update browser-eventpipe sample to use DOTNET_DiagnosticPorts
* remove unused imports
Diffstat (limited to 'src/mono')
-rw-r--r-- | src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj | 22 | ||||
-rw-r--r-- | src/mono/wasm/runtime/cwraps.ts | 4 | ||||
-rw-r--r-- | src/mono/wasm/runtime/diagnostics/index.ts | 87 | ||||
-rw-r--r-- | src/mono/wasm/runtime/driver.c | 6 | ||||
-rw-r--r-- | src/mono/wasm/runtime/memory.ts | 16 | ||||
-rw-r--r-- | src/mono/wasm/runtime/startup.ts | 18 |
6 files changed, 128 insertions, 25 deletions
diff --git a/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj b/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj index f3feef21cce..eb58b28f8cf 100644 --- a/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj +++ b/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj @@ -20,25 +20,19 @@ <ItemGroup> <WasmExtraFilesToDeploy Include="index.html" /> <WasmExtraFilesToDeploy Include="mock.js" Condition="'$(MonoDiagnosticsMock)' == 'true'"/> - <WasmExtraConfig Condition="true" Include="environmentVariables" Value=' -{ - "MONO_LOG_LEVEL": "warning", - "MONO_LOG_MASK": "all" -}' /> <!-- this option requires running dotnet-dsrouter and a real dotnet-trace client --> - <WasmExtraConfig Condition="true and '$(MonoDiagnosticsMock)' != 'true'" Include="diagnosticOptions" Value=' + <WasmExtraConfig Condition="'$(MonoDiagnosticsMock)' != 'true'" Include="environmentVariables" Value=' { - "server": { "suspend": true, "connectUrl": "ws://localhost:8088/diagnostics" } + "MONO_LOG_LEVEL": "warning", + "MONO_LOG_MASK": "all", + "DOTNET_DiagnosticPorts": "ws://localhost:8088/diagnostics,suspend" }' /> <!-- this option requires compiling the runtime with /p:MonoDiagnosticsMock=true and also building this project with the same property--> - <WasmExtraConfig Condition="true and '$(MonoDiagnosticsMock)' == 'true'" Include="diagnosticOptions" Value=' + <WasmExtraConfig Condition="'$(MonoDiagnosticsMock)' == 'true'" Include="environmentVariables" Value=' { - "server": { "suspend": false, "connectUrl": "mock:./mock.js" } -}' /> - <!-- this option will create an EventPipe session at startup, that will dump its data into the Emscripten VFS --> - <WasmExtraConfig Condition="false" Include="diagnosticOptions" Value=' -{ - "sessions": [ { "collectRundownEvents": "true", "providers": "WasmHello::5:EventCounterIntervalSec=1" } ] + "MONO_LOG_LEVEL": "warning", + "MONO_LOG_MASK": "all", + "DOTNET_DiagnosticPorts": "mock:./mock.js,suspend" }' /> </ItemGroup> diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index ccc2e72d23f..d65947d498c 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -81,6 +81,7 @@ const fn_signatures: SigLine[] = [ //INTERNAL [false, "mono_wasm_exit", "void", ["number"]], + [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"]], @@ -189,6 +190,7 @@ export interface t_Cwraps { //INTERNAL mono_wasm_exit(exit_code: number): number; + 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; @@ -232,4 +234,4 @@ export function init_c_exports(): void { wf[name] = fce; } } -}
\ No newline at end of file +} diff --git a/src/mono/wasm/runtime/diagnostics/index.ts b/src/mono/wasm/runtime/diagnostics/index.ts index 20300048931..68f79ed9f01 100644 --- a/src/mono/wasm/runtime/diagnostics/index.ts +++ b/src/mono/wasm/runtime/diagnostics/index.ts @@ -94,12 +94,25 @@ export const diagnostics: Diagnostics = getDiagnostics(); let suspendOnStartup = false; let diagnosticsServerEnabled = false; -export async function mono_wasm_init_diagnostics(options: DiagnosticOptions): Promise<void> { +let diagnosticsInitialized = false; + +export async function mono_wasm_init_diagnostics(opts: "env" | DiagnosticOptions): Promise<void> { + if (diagnosticsInitialized) + return; if (!monoWasmThreads) { - console.warn("MONO_WASM: ignoring diagnostics options because this runtime does not support diagnostics", options); + console.warn("MONO_WASM: ignoring diagnostics options because this runtime does not support diagnostics", opts); return; } else { - if (!is_nullish(options.server)) { + let options: DiagnosticOptions | null; + if (opts === "env") { + options = diagnostic_options_from_environment(); + if (!options) + return; + } else { + options = opts; + } + diagnosticsInitialized = true; + if (!is_nullish(options?.server)) { if (options.server.connectUrl === undefined || typeof (options.server.connectUrl) !== "string") { throw new Error("server.connectUrl must be a string"); } @@ -130,6 +143,74 @@ function boolsyOption(x: string | boolean): boolean { throw new Error(`invalid option: "${x}", should be true, false, or "true" or "false"`); } +/// Parse environment variables for diagnostics configuration +/// +/// The environment variables are: +/// * DOTNET_DiagnosticPorts +/// +function diagnostic_options_from_environment(): DiagnosticOptions | null { + const val = memory.getEnv("DOTNET_DiagnosticPorts"); + if (is_nullish(val)) + return null; + // TODO: consider also parsing the DOTNET_EnableEventPipe and DOTNET_EventPipeOutputPath, DOTNET_EvnetPipeConfig variables + // to configure the startup sessions that will dump output to the VFS. + return diagnostic_options_from_ports_spec(val); +} + +/// Parse a DOTNET_DiagnosticPorts string and return a DiagnosticOptions object. +/// See https://docs.microsoft.com/en-us/dotnet/core/diagnostics/diagnostic-port#configure-additional-diagnostic-ports +function diagnostic_options_from_ports_spec(val: string): DiagnosticOptions | null { + if (val === "") + return null; + const ports = val.split(";"); + if (ports.length === 0) + return null; + if (ports.length !== 1) { + console.warn("MONO_WASM: multiple diagnostic ports specified, only the last one will be used"); + } + const portSpec = ports[ports.length - 1]; + const components = portSpec.split(","); + if (components.length < 1 || components.length > 3) { + console.warn("MONO_WASM: invalid diagnostic port specification, should be of the form <port>[,<connect>],[<nosuspend|suspend>]"); + return null; + } + const uri: string = components[0]; + let connect = true; + let suspend = true; + // the C Diagnostic Server goes through these parts in reverse, do the same here. + for (let i = components.length - 1; i >= 1; i--) { + const component = components[i]; + switch (component.toLowerCase()) { + case "nosuspend": + suspend = false; + break; + case "suspend": + suspend = true; + break; + case "listen": + connect = false; + break; + case "connect": + connect = true; + break; + default: + console.warn(`MONO_WASM: invalid diagnostic port specification component: ${component}`); + break; + } + } + if (!connect) { + console.warn("MONO_WASM: this runtime does not support listening on a diagnostic port; no diagnostic server started"); + return null; + } + return { + server: { + connectUrl: uri, + suspend: suspend, + } + }; + +} + export function mono_wasm_diagnostic_server_on_runtime_server_init(out_options: VoidPtr): void { if (diagnosticsServerEnabled) { /* called on the main thread when the runtime is sufficiently initialized */ diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index a4108177548..0ddc467ae59 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -281,6 +281,12 @@ mono_wasm_setenv (const char *name, const char *value) monoeg_g_setenv (strdup (name), strdup (value), 1); } +EMSCRIPTEN_KEEPALIVE char * +mono_wasm_getenv (const char *name) +{ + return monoeg_g_getenv (name); // JS must free +} + static void *sysglobal_native_handle; static void* diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts index 95373c22fb7..2ac62de35f7 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -1,7 +1,7 @@ import monoWasmThreads from "consts:monoWasmThreads"; import { Module, runtimeHelpers } from "./imports"; import { mono_assert, MemOffset, NumberOrPointer } from "./types"; -import { VoidPtr } from "./types/emscripten"; +import { VoidPtr, CharPtr } from "./types/emscripten"; import * as cuint64 from "./cuint64"; import cwraps, { I52Error } from "./cwraps"; @@ -263,6 +263,18 @@ export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr { return memoryOffset; } +export function getEnv(name: string): string | null { + let charPtr: CharPtr = <any>0; + try { + charPtr = cwraps.mono_wasm_getenv(name); + if (<any>charPtr === 0) + return null; + else return Module.UTF8ToString(charPtr); + } finally { + if (charPtr) Module._free(<any>charPtr); + } +} + const BuiltinAtomics = globalThis.Atomics; export const Atomics = monoWasmThreads ? { @@ -276,4 +288,4 @@ export const Atomics = monoWasmThreads ? { } : { storeI32: setI32, notifyI32: () => { /*empty*/ } -};
\ No newline at end of file +}; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index b6e6f688030..051c0a796c5 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import MonoWasmThreads from "consts:monoWasmThreads"; import { mono_assert, CharPtrNull, DotnetModule, MonoConfig, MonoConfigError, LoadingResource, AssetEntry, ResourceRequest } from "./types"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, Module, runtimeHelpers } from "./imports"; import cwraps, { init_c_exports } from "./cwraps"; @@ -25,6 +26,7 @@ import { cwraps_internal } from "./exports-internal"; import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy"; import { DotnetPublicAPI } from "./export-types"; import { BINDING, MONO } from "./net6-legacy/imports"; +import { mono_wasm_init_diagnostics } from "./diagnostics"; let all_assets_loaded_in_memory: Promise<void> | null = null; const loaded_files: { url: string, file: string }[] = []; @@ -130,8 +132,8 @@ function preInit(isCustomStartup: boolean, userPreInit: (() => void)[]) { abort_startup(err, true); throw err; } - // this will start immediately but return on first await. - // It will block our `preRun` by afterPreInit promise + // this will start immediately but return on first await. + // It will block our `preRun` by afterPreInit promise // It will block emscripten `userOnRuntimeInitialized` by pending addRunDependency("mono_pre_init") (async () => { try { @@ -196,7 +198,7 @@ async function onRuntimeInitializedAsync(isCustomStartup: boolean, userOnRuntime _print_error("MONO_WASM: user callback onRuntimeInitialized() failed", err); throw err; } - // finish + // finish await mono_wasm_after_user_runtime_initialized(); } catch (err) { _print_error("MONO_WASM: onRuntimeInitializedAsync() failed", err); @@ -326,6 +328,10 @@ async function mono_wasm_after_user_runtime_initialized(): Promise<void> { } } } + // for Blazor, init diagnostics after their "onRuntimeInitalized" sets env variables, but before their postRun callback (which calls mono_wasm_load_runtime) + if (MonoWasmThreads) { + await mono_wasm_init_diagnostics("env"); + } if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: Initializing mono runtime"); @@ -532,8 +538,10 @@ async function _apply_configuration_from_args() { if (config.coverageProfilerOptions) mono_wasm_init_coverage_profiler(config.coverageProfilerOptions); - - // FIXME await mono_wasm_init_diagnostics(config.diagnosticOptions); + // for non-Blazor, init diagnostics after environment variables are set + if (MonoWasmThreads) { + await mono_wasm_init_diagnostics("env"); + } } export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): void { |