Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs2
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs30
-rw-r--r--src/mono/mono/metadata/threads.c6
-rw-r--r--src/mono/mono/utils/mono-threads-wasm.c17
-rw-r--r--src/mono/mono/utils/mono-threads-wasm.h4
-rw-r--r--src/mono/mono/utils/mono-threads.c9
-rw-r--r--src/mono/mono/utils/mono-threads.h1
-rw-r--r--src/mono/sample/wasm/browser-eventpipe/Program.cs91
-rw-r--r--src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj1
-rw-r--r--src/mono/sample/wasm/browser-eventpipe/index.html6
-rw-r--r--src/mono/sample/wasm/browser-eventpipe/main.js105
-rw-r--r--src/mono/wasm/runtime/diagnostics.ts136
-rw-r--r--src/mono/wasm/runtime/dotnet.d.ts33
-rw-r--r--src/mono/wasm/runtime/driver.c2
-rw-r--r--src/mono/wasm/runtime/types.ts4
15 files changed, 379 insertions, 68 deletions
diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs
index cf7b4471ff7..2bd67dc51c7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs
@@ -158,7 +158,7 @@ namespace System.Diagnostics.Tracing
#if ES_BUILD_STANDALONE
s_pollingThread.Start();
#else
- s_pollingThread.UnsafeStart();
+ s_pollingThread.InternalUnsafeStart();
#endif
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
index 8eb66011e42..0486c06bacb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
@@ -153,20 +153,24 @@ namespace System.Threading
Initialize();
}
-#if !TARGET_BROWSER
- internal static bool IsThreadStartSupported => true;
-#else
-#if FEATURE_WASM_THREADS
+#if !TARGET_BROWSER || FEATURE_WASM_THREADS
internal static bool IsThreadStartSupported => true;
+ internal static bool IsInternalThreadStartSupported => true;
+#elif FEATURE_WASM_PERFTRACING
+ internal static bool IsThreadStartSupported => false;
+ internal static bool IsInternalThreadStartSupported => true;
#else
internal static bool IsThreadStartSupported => false;
-#endif
+ internal static bool IsInternalThreadStartSupported => false;
#endif
- internal static void ThrowIfNoThreadStart()
+ internal static void ThrowIfNoThreadStart(bool internalThread = false)
{
- if (!IsThreadStartSupported)
- throw new PlatformNotSupportedException();
+ if (IsThreadStartSupported)
+ return;
+ if (IsInternalThreadStartSupported && internalThread)
+ return;
+ throw new PlatformNotSupportedException();
}
/// <summary>Causes the operating system to change the state of the current instance to <see cref="ThreadState.Running"/>, and optionally supplies an object containing data to be used by the method the thread executes.</summary>
@@ -187,9 +191,9 @@ namespace System.Threading
/// </remarks>
public void UnsafeStart(object? parameter) => Start(parameter, captureContext: false);
- private void Start(object? parameter, bool captureContext)
+ private void Start(object? parameter, bool captureContext, bool internalThread = false)
{
- ThrowIfNoThreadStart();
+ ThrowIfNoThreadStart(internalThread);
StartHelper? startHelper = _startHelper;
@@ -224,9 +228,11 @@ namespace System.Threading
/// </remarks>
public void UnsafeStart() => Start(captureContext: false);
- private void Start(bool captureContext)
+ internal void InternalUnsafeStart() => Start(captureContext: false, internalThread: true);
+
+ private void Start(bool captureContext, bool internalThread = false)
{
- ThrowIfNoThreadStart();
+ ThrowIfNoThreadStart(internalThread);
StartHelper? startHelper = _startHelper;
// In the case of a null startHelper (second call to start on same thread)
diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c
index f4a1f891cd4..378cec13b1e 100644
--- a/src/mono/mono/metadata/threads.c
+++ b/src/mono/mono/metadata/threads.c
@@ -4819,7 +4819,11 @@ ves_icall_System_Threading_Thread_StartInternal (MonoThreadObjectHandle thread_h
MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
gboolean res;
-#if defined (DISABLE_THREADS) || defined (DISABLE_WASM_USER_THREADS)
+#if defined (DISABLE_THREADS)
+ /*
+ * N.B. not checking DISABLE_WASM_USER_THREADS - managed utility threads are allowed; we assume all
+ * callers of System.Thread.StartCore called System.Thread.ThrowIfNotSupported(bool internalThread)
+ */
mono_error_set_platform_not_supported (error, "Cannot start threads on this runtime.");
return;
#endif
diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c
index 496daaf42d3..a6b6552032b 100644
--- a/src/mono/mono/utils/mono-threads-wasm.c
+++ b/src/mono/mono/utils/mono-threads-wasm.c
@@ -393,6 +393,23 @@ mono_threads_wasm_is_browser_thread (void)
#endif
}
+MonoNativeThreadId
+mono_threads_wasm_browser_thread_tid (void)
+{
+#ifdef DISABLE_THREADS
+ return (MonoNativeThreadId)1;
+#else
+ return (MonoNativeThreadId)emscripten_main_browser_thread_id ();
+#endif
+}
+
+gboolean
+mono_threads_platform_stw_defer_initial_suspend (MonoThreadInfo *info)
+{
+ /* Suspend the browser thread after all the other threads are suspended already. */
+ return mono_native_thread_id_equals (mono_thread_info_get_tid (info), mono_threads_wasm_browser_thread_tid ());
+}
+
#ifndef DISABLE_THREADS
void
mono_threads_wasm_async_run_in_main_thread (void (*func) (void))
diff --git a/src/mono/mono/utils/mono-threads-wasm.h b/src/mono/mono/utils/mono-threads-wasm.h
index ecad01a7ec6..1f5cf6bb0c4 100644
--- a/src/mono/mono/utils/mono-threads-wasm.h
+++ b/src/mono/mono/utils/mono-threads-wasm.h
@@ -9,6 +9,7 @@
#define __MONO_THREADS_WASM_H__
#include <glib.h>
+#include <mono/utils/mono-threads.h>
#ifdef HOST_WASM
@@ -22,6 +23,9 @@
gboolean
mono_threads_wasm_is_browser_thread (void);
+MonoNativeThreadId
+mono_threads_wasm_browser_thread_tid (void);
+
#ifndef DISABLE_THREADS
/**
* Runs the given function asynchronously on the main thread.
diff --git a/src/mono/mono/utils/mono-threads.c b/src/mono/mono/utils/mono-threads.c
index a947b574c1b..435b7dca5a9 100644
--- a/src/mono/mono/utils/mono-threads.c
+++ b/src/mono/mono/utils/mono-threads.c
@@ -1099,6 +1099,8 @@ begin_suspend_peek_and_preempt (MonoThreadInfo *info);
MonoThreadBeginSuspendResult
mono_thread_info_begin_suspend (MonoThreadInfo *info, MonoThreadSuspendPhase phase)
{
+ if (phase == MONO_THREAD_SUSPEND_PHASE_INITIAL && mono_threads_platform_stw_defer_initial_suspend (info))
+ return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE;
if (phase == MONO_THREAD_SUSPEND_PHASE_MOPUP && mono_threads_is_hybrid_suspension_enabled ())
return begin_suspend_peek_and_preempt (info);
else
@@ -2171,3 +2173,10 @@ mono_thread_info_get_tools_data (void)
return info ? info->tools_data : NULL;
}
+#ifndef HOST_WASM
+gboolean
+mono_threads_platform_stw_defer_initial_suspend (MonoThreadInfo *info)
+{
+ return FALSE;
+}
+#endif
diff --git a/src/mono/mono/utils/mono-threads.h b/src/mono/mono/utils/mono-threads.h
index 36cc46affe0..acd3d6e66b7 100644
--- a/src/mono/mono/utils/mono-threads.h
+++ b/src/mono/mono/utils/mono-threads.h
@@ -634,6 +634,7 @@ void mono_threads_platform_init (void);
gboolean mono_threads_platform_in_critical_region (THREAD_INFO_TYPE *info);
gboolean mono_threads_platform_yield (void);
void mono_threads_platform_exit (gsize exit_code);
+gboolean mono_threads_platform_stw_defer_initial_suspend (THREAD_INFO_TYPE *info);
void mono_threads_coop_begin_global_suspend (void);
void mono_threads_coop_end_global_suspend (void);
diff --git a/src/mono/sample/wasm/browser-eventpipe/Program.cs b/src/mono/sample/wasm/browser-eventpipe/Program.cs
index 2755fcd7e25..0feecf5c66e 100644
--- a/src/mono/sample/wasm/browser-eventpipe/Program.cs
+++ b/src/mono/sample/wasm/browser-eventpipe/Program.cs
@@ -5,10 +5,65 @@ using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
+using System.Diagnostics.Tracing;
namespace Sample
{
+
+ [EventSource(Name = "WasmHello")]
+ public class WasmHelloEventSource : EventSource
+ {
+ public static readonly WasmHelloEventSource Instance = new ();
+
+ private IncrementingEventCounter _calls;
+
+ private WasmHelloEventSource ()
+ {
+ }
+
+ [NonEvent]
+ public void NewCallsCounter()
+ {
+ _calls?.Dispose();
+ _calls = new ("fib-calls", this)
+ {
+ DisplayName = "Recursive Fib calls",
+ };
+ }
+
+ [NonEvent]
+ public void CountCall() {
+ _calls?.Increment(1.0);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ _calls?.Dispose();
+ _calls = null;
+
+ base.Dispose(disposing);
+ }
+
+ [Event(1, Message="Started Fib({0})", Level = EventLevel.Informational)]
+ public void StartFib(int n)
+ {
+ if (!IsEnabled())
+ return;
+
+ WriteEvent(1, n);
+ }
+
+ [Event(2, Message="Stopped Fib({0}) = {1}", Level = EventLevel.Informational)]
+ public void StopFib(int n, string result)
+ {
+ if (!IsEnabled())
+ return;
+
+ WriteEvent(2, n, result);
+ }
+ }
+
public class Test
{
public static void Main(string[] args)
@@ -34,24 +89,34 @@ namespace Sample
return 0;
if (n == 1)
return 1;
+ WasmHelloEventSource.Instance.CountCall();
return recursiveFib (n - 1) + recursiveFib (n - 2);
}
- public static async Task<int> StartAsyncWork()
+ public static async Task<double> StartAsyncWork(int N)
{
CancellationToken ct = GetCancellationToken();
+ await Task.Delay(1);
long b;
- const int N = 35;
- const long expected = 9227465;
+ WasmHelloEventSource.Instance.NewCallsCounter();
+ iterations = 0;
while (true)
{
- await Task.Delay(1).ConfigureAwait(false);
+ WasmHelloEventSource.Instance.StartFib(N);
+ await Task.Delay(1);
b = recursiveFib (N);
+ WasmHelloEventSource.Instance.StopFib(N, b.ToString());
+ iterations++;
if (ct.IsCancellationRequested)
break;
- iterations++;
}
- return b == expected ? 42 : 0;
+ long expected = fastFib(N);
+ if (expected == b)
+ return (double)b;
+ else {
+ Console.Error.WriteLine ("expected {0}, but got {1}", expected, b);
+ return 0.0;
+ }
}
public static void StopWork()
@@ -63,5 +128,19 @@ namespace Sample
{
return iterations.ToString();
}
+
+ private static long fastFib(int N) {
+ if (N < 1)
+ return 0;
+ long a = 0;
+ long b = 1;
+ for (int i = 1; i < N; ++i) {
+ long tmp = a+b;
+ a = b;
+ b = tmp;
+ }
+ return b;
+ }
+
}
}
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 477e50104ed..d59d8baafb3 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
@@ -10,6 +10,7 @@
<GenerateRunScriptForSample Condition="'$(ArchiveTests)' == 'true'">true</GenerateRunScriptForSample>
<RunScriptCommand>$(ExecXHarnessCmd) wasm test-browser --app=. --browser=Chrome $(XHarnessBrowserPathArg) --html-file=index.html --output-directory=$(XHarnessOutput) -- $(MSBuildProjectName).dll</RunScriptCommand>
<FeatureWasmPerfTracing>true</FeatureWasmPerfTracing>
+ <NoWarn>CA2007</NoWarn> <!-- consider ConfigureAwait() -->
</PropertyGroup>
<ItemGroup>
diff --git a/src/mono/sample/wasm/browser-eventpipe/index.html b/src/mono/sample/wasm/browser-eventpipe/index.html
index 87ef4b46638..7d8904b1cca 100644
--- a/src/mono/sample/wasm/browser-eventpipe/index.html
+++ b/src/mono/sample/wasm/browser-eventpipe/index.html
@@ -11,7 +11,11 @@
<body>
<h3 id="header">Wasm Browser EventPipe profiling Sample</h3>
- Computing Fib repeatedly: <span id="out"></span>
+ <div>
+ <button id="startWork">Start Work</button>
+ <label for="inputN">N:</label> <input type="number" id="inputN" value="10"/>
+ </div>
+ <div>Computing Fib(N) repeatedly: <span id="out"></span>
<script type="text/javascript" src="./dotnet.js"></script>
<script type="text/javascript" src="./dotnet.worker.js"></script>
<script type="text/javascript" src="./main.js"></script>
diff --git a/src/mono/sample/wasm/browser-eventpipe/main.js b/src/mono/sample/wasm/browser-eventpipe/main.js
index ef8029db509..c046f84731e 100644
--- a/src/mono/sample/wasm/browser-eventpipe/main.js
+++ b/src/mono/sample/wasm/browser-eventpipe/main.js
@@ -8,8 +8,7 @@ function wasm_exit(exit_code) {
console.log(`WASM EXIT ${exit_code}`);
}
-function downloadData(dataURL,filename)
-{
+function downloadData(dataURL, filename) {
// make an `<a download="filename" href="data:..."/>` link and click on it to trigger a download with the given name
const elt = document.createElement('a');
elt.download = filename;
@@ -22,8 +21,7 @@ function downloadData(dataURL,filename)
document.body.removeChild(elt);
}
-function makeTimestamp()
-{
+function makeTimestamp() {
// ISO date string, but with : and . replaced by -
const t = new Date();
const s = t.toISOString();
@@ -37,66 +35,85 @@ async function loadRuntime() {
}
-const delay = (ms) => new Promise((resolve) => setTimeout (resolve, ms))
+const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
-const saveUsingBlob = true;
+async function doWork(startWork, stopWork, getIterationsDone) {
+ const N = parseInt(document.getElementById("inputN").value);
-async function main() {
- const createDotnetRuntime = await loadRuntime();
- const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime(() => {
- console.log('user code in createDotnetRuntime')
- return {
- disableDotnet6Compatibility: true,
- configSrc: "./mono-config.json",
- preInit: () => { console.log('user code Module.preInit') },
- preRun: () => { console.log('user code Module.preRun') },
- onRuntimeInitialized: () => { console.log('user code Module.onRuntimeInitialized') },
- postRun: () => { console.log('user code Module.postRun') },
- }
- });
- globalThis.__Module = Module;
- globalThis.MONO = MONO;
- console.log('after createDotnetRuntime')
- const startWork = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:StartAsyncWork");
- const stopWork = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:StopWork");
- const getIterationsDone = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:GetIterationsDone");
- const eventSession = MONO.diagnostics.createEventPipeSession();
- eventSession.start();
- const workPromise = startWork();
+ const workPromise = startWork(N);
+
+ let btn = document.getElementById("startWork");
+ btn.disabled = true;
+ btn.innerText = "Working";
+ document.getElementById("out").innerHTML = '...';
- document.getElementById("out").innerHTML = '&lt;&lt;running&gt;&gt;';
await delay(5000); // let it run for 5 seconds
- stopWork();
+ document.getElementById("startWork").innerText = "Stopping";
+ document.getElementById("out").innerHTML = '... ...';
- document.getElementById("out").innerHTML = '&lt;&lt;stopping&gt;&gt;';
+ stopWork();
const ret = await workPromise; // get the answer
const iterations = getIterationsDone(); // get how many times the loop ran
- eventSession.stop();
+ btn = document.getElementById("startWork");
+ btn.disabled = false;
+ btn.innerText = "Start Work";
- document.getElementById("out").innerHTML = `${ret} as computed in ${iterations} iterations on dotnet ver ${RuntimeBuildInfo.ProductVersion}`;
+ document.getElementById("out").innerHTML = `${ret} as computed in ${iterations} iterations`;
console.debug(`ret: ${ret}`);
- const filename = "dotnet-wasm-" + makeTimestamp() + ".nettrace";
+ return ret;
+}
+
+function getOnClickHandler(startWork, stopWork, getIterationsDone) {
+ return async function () {
+ const options = MONO.diagnostics.SessionOptionsBuilder
+ .Empty
+ .setRundownEnabled(false)
+ .addProvider({ name: 'WasmHello', level: MONO.diagnostics.EventLevel.Verbose, args: 'EventCounterIntervalSec=1' })
+ .build();
+ console.log('starting providers', options.providers);
+
+ const eventSession = MONO.diagnostics.createEventPipeSession(options);
+
+ eventSession.start();
+ const ret = await doWork(startWork, stopWork, getIterationsDone);
+ eventSession.stop();
+
+ const filename = "dotnet-wasm-" + makeTimestamp() + ".nettrace";
- if (saveUsingBlob) {
const blob = eventSession.getTraceBlob();
const uri = URL.createObjectURL(blob);
downloadData(uri, filename);
- URL.revokeObjectURL(uri);
- } else {
- const dataUri = eventSession.getTraceDataURI();
-
- downloadData(dataUri, filename);
}
- const exit_code = ret == 42 ? 0 : 1;
+}
+
+
+async function main() {
+ const createDotnetRuntime = await loadRuntime();
+ const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime(() => {
+ return {
+ disableDotnet6Compatibility: true,
+ configSrc: "./mono-config.json",
+ }
+ });
+ globalThis.__Module = Module;
+ globalThis.MONO = MONO;
+
+ const startWork = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:StartAsyncWork");
+ const stopWork = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:StopWork");
+ const getIterationsDone = BINDING.bind_static_method("[Wasm.Browser.EventPipe.Sample] Sample.Test:GetIterationsDone");
+
+
+ const btn = document.getElementById("startWork");
+
+ btn.style.backgroundColor = "rgb(192,255,192)";
+ btn.onclick = getOnClickHandler(startWork, stopWork, getIterationsDone);
- wasm_exit(exit_code);
}
-console.log("Waiting 10s for curious human before starting the program");
-setTimeout(main, 10000);
+main();
diff --git a/src/mono/wasm/runtime/diagnostics.ts b/src/mono/wasm/runtime/diagnostics.ts
index eee3f061b97..d346a986546 100644
--- a/src/mono/wasm/runtime/diagnostics.ts
+++ b/src/mono/wasm/runtime/diagnostics.ts
@@ -82,18 +82,136 @@ class EventPipeFileSession implements EventPipeSession {
}
}
+const eventLevel = {
+ LogAlways: 0,
+ Critical: 1,
+ Error: 2,
+ Warning: 3,
+ Informational: 4,
+ Verbose: 5,
+} as const;
+
+type EventLevel = typeof eventLevel;
+
+type UnnamedProviderConfiguration = Partial<{
+ keyword_mask: string | 0;
+ level: number;
+ args: string;
+}>
+
+/// The configuration for an individual provider. Each provider configuration has the name of the provider,
+/// the level of events to collect, and a string containing a 32-bit hexadecimal mask (without an "0x" prefix) of
+/// the "keywords" to filter a subset of the events. The keyword mask may be the number 0 or "" to skips the filtering.
+/// See https://docs.microsoft.com/en-us/dotnet/core/diagnostics/well-known-event-providers for a list of known providers.
+/// Additional providers may be added by applications or libraries that implement an EventSource subclass.
+/// See https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventsource?view=net-6.0
+///
+/// Some providers also have an "args" string in an arbitrary format. For example the EventSource providers that
+/// include EventCounters have a "EventCounterIntervalSec=NNN" argument that specified how often the counters of
+/// the event source should be polled.
+export interface ProviderConfiguration extends UnnamedProviderConfiguration {
+ name: string;
+}
+
+const runtimeProviderName = "Microsoft-Windows-DotNETRuntime";
+const runtimePrivateProviderName = "Microsoft-Windows-DotNETRuntimePrivate";
+const sampleProfilerProviderName = "Microsoft-DotNETCore-SampleProfiler";
+
+const runtimeProviderDefault: ProviderConfiguration = {
+ name: runtimeProviderName,
+ keyword_mask: "4c14fccbd",
+ level: eventLevel.Verbose,
+};
+
+const runtimePrivateProviderDefault: ProviderConfiguration = {
+ name: runtimePrivateProviderName,
+ keyword_mask: "4002000b",
+ level: eventLevel.Verbose,
+};
+
+const sampleProfilerProviderDefault: ProviderConfiguration = {
+ name: sampleProfilerProviderName,
+ keyword_mask: "0",
+ level: eventLevel.Verbose,
+};
+
+/// A helper class to create EventPipeSessionOptions
+export class SessionOptionsBuilder {
+ private _rundown?: boolean;
+ private _providers: ProviderConfiguration[];
+ /// Create an empty builder. Prefer to use SesssionOptionsBuilder.Empty
+ constructor() {
+ this._providers = [];
+ }
+ /// Gets a builder with no providers.
+ static get Empty(): SessionOptionsBuilder { return new SessionOptionsBuilder(); }
+ /// Gets a builder with default providers and rundown events enabled.
+ /// See https://docs.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables
+ static get DefaultProviders(): SessionOptionsBuilder {
+ return this.Empty.addRuntimeProvider().addRuntimePrivateProvider().addSampleProfilerProvider();
+ }
+ /// Change whether to collect rundown events.
+ /// Certain providers may need rundown events to be collected in order to provide useful diagnostic information.
+ setRundownEnabled(enabled: boolean): SessionOptionsBuilder {
+ this._rundown = enabled;
+ return this;
+ }
+ /// Add a provider configuration to the builder.
+ addProvider(provider: ProviderConfiguration): SessionOptionsBuilder {
+ this._providers.push(provider);
+ return this;
+ }
+ /// Add the Microsoft-Windows-DotNETRuntime provider. Use override options to change the event level or keyword mask.
+ /// The default is { keyword_mask: "4c14fccbd", level: eventLevel.Verbose }
+ addRuntimeProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder {
+ const options = { ...runtimeProviderDefault, ...overrideOptions };
+ this._providers.push(options);
+ return this;
+ }
+ /// Add the Microsoft-Windows-DotNETRuntimePrivate provider. Use override options to change the event level or keyword mask.
+ /// The default is { keyword_mask: "4002000b", level: eventLevel.Verbose}
+ addRuntimePrivateProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder {
+ const options = { ...runtimePrivateProviderDefault, ...overrideOptions };
+ this._providers.push(options);
+ return this;
+ }
+ /// Add the Microsoft-DotNETCore-SampleProfiler. Use override options to change the event level or keyword mask.
+ // The default is { keyword_mask: 0, level: eventLevel.Verbose }
+ addSampleProfilerProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder {
+ const options = { ...sampleProfilerProviderDefault, ...overrideOptions };
+ this._providers.push(options);
+ return this;
+ }
+ /// Create an EventPipeSessionOptions from the builder.
+ build(): EventPipeSessionOptions {
+ const providers = this._providers.map(p => {
+ const name = p.name;
+ const keyword_mask = "" + (p?.keyword_mask ?? "");
+ const level = p?.level ?? eventLevel.Verbose;
+ const args = p?.args ?? "";
+ const maybeArgs = args != "" ? `:${args}` : "";
+ return `${name}:${keyword_mask}:${level}${maybeArgs}`;
+ });
+ return {
+ collectRundownEvents: this._rundown,
+ providers: providers.join(",")
+ };
+ }
+}
+
// a conter for the number of sessions created
let totalSessions = 0;
function createSessionWithPtrCB(sessionIdOutPtr: VoidPtr, options: EventPipeSessionOptions | undefined, tracePath: string): false | number {
const defaultRundownRequested = true;
- const defaultProviders = "";
+ const defaultProviders = ""; // empty string means use the default providers
const defaultBufferSizeInMB = 1;
const rundown = options?.collectRundownEvents ?? defaultRundownRequested;
+ const providers = options?.providers ?? defaultProviders;
memory.setI32(sessionIdOutPtr, 0);
- if (!cwraps.mono_wasm_event_pipe_enable(tracePath, defaultBufferSizeInMB, defaultProviders, rundown, sessionIdOutPtr)) {
+ if (!cwraps.mono_wasm_event_pipe_enable(tracePath, defaultBufferSizeInMB, providers, rundown, sessionIdOutPtr)) {
return false;
} else {
return memory.getI32(sessionIdOutPtr);
@@ -101,11 +219,25 @@ function createSessionWithPtrCB(sessionIdOutPtr: VoidPtr, options: EventPipeSess
}
export interface Diagnostics {
+ EventLevel: EventLevel;
+ SessionOptionsBuilder: typeof SessionOptionsBuilder;
+
createEventPipeSession(options?: EventPipeSessionOptions): EventPipeSession | null;
}
+
/// APIs for working with .NET diagnostics from JavaScript.
export const diagnostics: Diagnostics = {
+ /// An enumeration of the level (higher value means more detail):
+ /// LogAlways: 0,
+ /// Critical: 1,
+ /// Error: 2,
+ /// Warning: 3,
+ /// Informational: 4,
+ /// Verbose: 5,
+ EventLevel: eventLevel,
+ /// A builder for creating an EventPipeSessionOptions instance.
+ SessionOptionsBuilder: SessionOptionsBuilder,
/// Creates a new EventPipe session that will collect trace events from the runtime and managed libraries.
/// Use the options to control the kinds of events to be collected.
/// Multiple sessions may be created and started at the same time.
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 19d6ddbc90b..34d62ebad8e 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -210,6 +210,7 @@ declare type CoverageProfilerOptions = {
};
interface EventPipeSessionOptions {
collectRundownEvents?: boolean;
+ providers: string;
}
declare type DotnetModuleConfig = {
disableDotnet6Compatibility?: boolean;
@@ -249,7 +250,39 @@ interface EventPipeSession {
stop(): void;
getTraceBlob(): Blob;
}
+declare const eventLevel: {
+ readonly LogAlways: 0;
+ readonly Critical: 1;
+ readonly Error: 2;
+ readonly Warning: 3;
+ readonly Informational: 4;
+ readonly Verbose: 5;
+};
+declare type EventLevel = typeof eventLevel;
+declare type UnnamedProviderConfiguration = Partial<{
+ keyword_mask: string | 0;
+ level: number;
+ args: string;
+}>;
+interface ProviderConfiguration extends UnnamedProviderConfiguration {
+ name: string;
+}
+declare class SessionOptionsBuilder {
+ private _rundown?;
+ private _providers;
+ constructor();
+ static get Empty(): SessionOptionsBuilder;
+ static get DefaultProviders(): SessionOptionsBuilder;
+ setRundownEnabled(enabled: boolean): SessionOptionsBuilder;
+ addProvider(provider: ProviderConfiguration): SessionOptionsBuilder;
+ addRuntimeProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder;
+ addRuntimePrivateProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder;
+ addSampleProfilerProvider(overrideOptions?: UnnamedProviderConfiguration): SessionOptionsBuilder;
+ build(): EventPipeSessionOptions;
+}
interface Diagnostics {
+ EventLevel: EventLevel;
+ SessionOptionsBuilder: typeof SessionOptionsBuilder;
createEventPipeSession(options?: EventPipeSessionOptions): EventPipeSession | null;
}
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index 605169be251..32528ad0559 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -476,7 +476,7 @@ mono_wasm_load_runtime (const char *unused, int debug_level)
// We should enable this as part of the wasm build later
#ifndef DISABLE_THREADS
monoeg_g_setenv ("MONO_THREADS_SUSPEND", "coop", 0);
- monoeg_g_setenv ("MONO_SLEEP_ABORT_LIMIT", "250", 0);
+ monoeg_g_setenv ("MONO_SLEEP_ABORT_LIMIT", "5000", 0);
#endif
#ifdef DEBUG
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index 2abb34e662c..8fb000c1982 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -180,10 +180,14 @@ export type CoverageProfilerOptions = {
}
/// Options to configure the event pipe session
+/// The recommended method is to MONO.diagnostics.SesisonOptionsBuilder to create an instance of this type
export interface EventPipeSessionOptions {
/// Whether to collect additional details (such as method and type names) at EventPipeSession.stop() time (default: true)
/// This is required for some use cases, and may allow some tools to better understand the events.
collectRundownEvents?: boolean;
+ /// The providers that will be used by this session.
+ /// See https://docs.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables
+ providers: string;
}
// how we extended emscripten Module