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:
authorAleksey Kliger (λgeek) <alklig@microsoft.com>2022-06-02 02:31:06 +0300
committerGitHub <noreply@github.com>2022-06-02 02:31:06 +0300
commit6726fae1e7fc8a8e80892f28215e18b89e3a61c7 (patch)
treea073a15ce6eeae80ca25740a27b86d21a4927f30 /src/mono/wasm/runtime
parent1631928292e94c19d8668d0dcee9fab0abfb4dd3 (diff)
[wasm-ep] Implement EventSource counters support; add an EventPipeSessionOptions builder to TS (#69567)
1. Implements support for creating event counters in WebAssembly apps. **Important change** adds `Thread.IsInternalThreadStartSupported` and `Thread.InternalUnsafeStart()` to `System.Threading.Thread` that can be used by System.Private.CoreLib to start threads on a runtime where `Thread.IsThreadStartSupported` is false. This is used to create the event counters polling thread. This is in addition to the native runtime being able to create threads using `mono_thread_create_internal` 2. Coop thread suspend: STW calls `mono_threads_platform_stw_defer_initial_suspend` which postpones suspending a thread until the next GC suspend phase. Doesn't do anything on most platforms. On WASM, suspend the main browser thread after all the other threads are suspended. This helps prevent deadlocks where the main thread is suspended while another thread is doing a call that is proxied to the main thread and cannot complete. By suspending the main thread last, we give it a chance to service the other threads' requests. 2. Adds a `MONO.diagnostics.SessionOptionsBuilder` class that can be used to create an `EventPipeSessionOptions` instance and populate the provider string for an event pipe session. 3. Updated the `browser-eventpipe` sample to create an EventSource and some counters. The sample is now interactive so you have to click "Start Work" in a browser for things to happen. There's an outstanding issue https://github.com/dotnet/runtime/issues/69568 that will be investigated in a follow-up PR * Add custom EventSource and counter - Added a method for coop GC to suspend the main browser thread in the second phase (so that other threads are less likely to deadlock if they delegate work to it) - Added an event provider builder - Added a Thread.InternalUnsafeStart() method and a IsInternalThreadStartSupported property. This allows EventSource to start a thread to poll the counter values. - The sample with counters can now record counter values. But not at the same time as the default configuration (runtime, runtime private, sample profiler). Not sure if it's a wasm issue or a limitation of EventPipe. * Change ProvidersConfigBuilder to SessionOptionsBuilder it creates an entire EventPipeSessionOptions object, not just the providers config * checkout interactive demo * Add docs; fix whitespace * more whitespace fixes * add default arg value to ThrowIfNoThreadStart * fix build * Review feedback
Diffstat (limited to 'src/mono/wasm/runtime')
-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
4 files changed, 172 insertions, 3 deletions
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