diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
index 9c0a120531003a..ef2e6039dbe67f 100644
--- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
+++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
@@ -512,6 +512,26 @@ ep_rt_sample_profiler_write_sampling_event_for_threads (
ep_rt_aot_sample_profiler_write_sampling_event_for_threads (sampling_thread, sampling_event);
}
+static
+inline
+void
+ep_rt_sample_profiler_enabled (void)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // no-op
+}
+
+static
+inline
+void
+ep_rt_sample_profiler_disabled (void)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // no-op
+}
+
static
inline
void
diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
index b45e9770a1c4e9..762b6295bb7e2f 100644
--- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
+++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
@@ -567,6 +567,26 @@ ep_rt_sample_profiler_write_sampling_event_for_threads (
ep_rt_coreclr_sample_profiler_write_sampling_event_for_threads (sampling_thread, sampling_event);
}
+static
+inline
+void
+ep_rt_sample_profiler_enabled (void)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // no-op
+}
+
+static
+inline
+void
+ep_rt_sample_profiler_disabled (void)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // no-op
+}
+
static
inline
void
diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
index 59edf88cf7e4f4..c444ac1488c2f3 100644
--- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -114,7 +114,7 @@
true
true
true
- true
+ true
true
@@ -135,7 +135,7 @@
-
+
diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj
index 95f56cea27e8cc..b9e72bdf650f36 100644
--- a/src/mono/browser/browser.proj
+++ b/src/mono/browser/browser.proj
@@ -424,6 +424,8 @@
<_CmakeEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE=0" Condition="'$(WasmEnableJsInteropByValue)' == 'false'"/>
<_CmakeEnvironmentVariable Include="WASM_ENABLE_SIMD=1" Condition="'$(WasmEnableSIMD)' != 'false'" />
<_CmakeEnvironmentVariable Include="WASM_ENABLE_SIMD=0" Condition="'$(WasmEnableSIMD)' == 'false'" />
+ <_CmakeEnvironmentVariable Include="FEATURE_PERFTRACING=1" Condition="'$(FeaturePerfTracing)' != 'false'" />
+ <_CmakeEnvironmentVariable Include="FEATURE_PERFTRACING=0" Condition="'$(FeaturePerfTracing)' == 'false'" />
<_CmakeEnvironmentVariable Include="WASM_ENABLE_EH=1" Condition="'$(WasmEnableExceptionHandling)' != 'false'" />
<_CmakeEnvironmentVariable Include="WASM_ENABLE_EH=0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" />
<_CmakeEnvironmentVariable Include="RUN_AOT_COMPILATION=1" Condition="'$(RunAOTCompilation)' == 'true'" />
@@ -568,6 +570,8 @@
<_MonoRollupEnvironmentVariable Include="WasmEnableThreads:$(WasmEnableThreads)" />
<_MonoRollupEnvironmentVariable Include="WASM_ENABLE_SIMD:1" Condition="'$(WasmEnableSIMD)' != 'false'" />
<_MonoRollupEnvironmentVariable Include="WASM_ENABLE_SIMD:0" Condition="'$(WasmEnableSIMD)' == 'false'" />
+ <_MonoRollupEnvironmentVariable Include="FEATURE_PERFTRACING:1" Condition="'$(FeaturePerfTracing)' != 'false'" />
+ <_MonoRollupEnvironmentVariable Include="FEATURE_PERFTRACING:0" Condition="'$(FeaturePerfTracing)' == 'false'" />
<_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:1" Condition="'$(WasmEnableExceptionHandling)' != 'false'" />
<_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" />
<_MonoRollupEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE:1" Condition="'$(WasmEnableJsInteropByValue)' == 'true'" />
diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets
index ac43cf31e1d11f..08df4ce7c3c85a 100644
--- a/src/mono/browser/build/BrowserWasmApp.targets
+++ b/src/mono/browser/build/BrowserWasmApp.targets
@@ -3,6 +3,8 @@
true
$(WasmEnableExceptionHandling)
+ true
+ false
@@ -362,6 +364,8 @@
+
+
diff --git a/src/mono/browser/build/WasmApp.InTree.props b/src/mono/browser/build/WasmApp.InTree.props
index b53cc29cbc9880..5833c9eff7cb0d 100644
--- a/src/mono/browser/build/WasmApp.InTree.props
+++ b/src/mono/browser/build/WasmApp.InTree.props
@@ -28,7 +28,7 @@
<_MonoRuntimeComponentDontLink Include="libmono-component-debugger-stub-static.a" />
- <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a"/>
+ <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-stub-static.a" />
<_MonoRuntimeComponentDontLink Include="libmono-component-hot_reload-stub-static.a" />
<_MonoRuntimeComponentDontLink Include="libmono-component-marshal-ilgen-stub-static.a" />
diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c
index e64c5f6d2d81a9..29db5546f09948 100644
--- a/src/mono/browser/runtime/corebindings.c
+++ b/src/mono/browser/runtime/corebindings.c
@@ -32,6 +32,7 @@ extern void mono_wasm_invoke_js_function (int function_js_handle, void *args);
extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error);
typedef void (*background_job_cb)(void);
+typedef int (*ds_job_cb)(void* data);
void mono_wasm_bind_assembly_exports (char *assembly_name);
void mono_wasm_assembly_get_entry_point (char *assembly_name, int auto_insert_breakpoint, MonoMethod **method_out);
diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts
index 19a24b08a11b13..b90143b89dad0d 100644
--- a/src/mono/browser/runtime/cwraps.ts
+++ b/src/mono/browser/runtime/cwraps.ts
@@ -42,6 +42,7 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_parse_runtime_options", null, ["number", "number"]],
[true, "mono_wasm_strdup", "number", ["string"]],
[true, "mono_background_exec", null, []],
+ [true, "mono_wasm_ds_exec", null, []],
[true, "mono_wasm_execute_timer", null, []],
[true, "mono_wasm_load_icu_data", "number", ["number"]],
[false, "mono_wasm_add_assembly", "number", ["string", "number", "number"]],
@@ -166,6 +167,7 @@ export interface t_Cwraps {
mono_wasm_strdup(value: string): number;
mono_wasm_parse_runtime_options(length: number, argv: VoidPtr): void;
mono_background_exec(): void;
+ mono_wasm_ds_exec(): void;
mono_wasm_execute_timer(): void;
mono_wasm_load_icu_data(offset: VoidPtr): number;
mono_wasm_add_assembly(name: string, data: VoidPtr, size: number): number;
diff --git a/src/mono/browser/runtime/es6/dotnet.es6.lib.js b/src/mono/browser/runtime/es6/dotnet.es6.lib.js
index ad8b0303bbb12d..bf5ceabaf44afd 100644
--- a/src/mono/browser/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/browser/runtime/es6/dotnet.es6.lib.js
@@ -8,6 +8,7 @@
// because we can't pass custom define symbols to acorn optimizer, we use environment variables to pass other build options
const WASM_ENABLE_SIMD = process.env.WASM_ENABLE_SIMD === "1";
+const FEATURE_PERFTRACING = process.env.FEATURE_PERFTRACING === "1";
const WASM_ENABLE_EH = process.env.WASM_ENABLE_EH === "1";
const ENABLE_BROWSER_PROFILER = process.env.ENABLE_BROWSER_PROFILER === "1";
const ENABLE_AOT_PROFILER = process.env.ENABLE_AOT_PROFILER === "1";
@@ -86,6 +87,7 @@ function injectDependencies() {
DotnetSupportLib["$DOTNET__postset"] = `DOTNET.setup({ ` +
`wasmEnableSIMD: ${WASM_ENABLE_SIMD ? "true" : "false"},` +
`wasmEnableEH: ${WASM_ENABLE_EH ? "true" : "false"},` +
+ `enablePerfTracing: ${FEATURE_PERFTRACING ? "true" : "false"}, ` +
`enableAotProfiler: ${ENABLE_AOT_PROFILER ? "true" : "false"}, ` +
`enableBrowserProfiler: ${ENABLE_BROWSER_PROFILER ? "true" : "false"}, ` +
`enableLogProfiler: ${ENABLE_LOG_PROFILER ? "true" : "false"}, ` +
diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts
index 03879c4381de0f..8e819c48f93c7f 100644
--- a/src/mono/browser/runtime/exports-binding.ts
+++ b/src/mono/browser/runtime/exports-binding.ts
@@ -13,7 +13,7 @@ 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_leave, mono_wasm_profiler_enter } from "./profiler";
+import { mono_wasm_profiler_leave, mono_wasm_profiler_enter, 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";
@@ -88,6 +88,13 @@ 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,
];
diff --git a/src/mono/browser/runtime/profiler.ts b/src/mono/browser/runtime/profiler.ts
index 0829b3ec71a66a..0b6c93af31e2ff 100644
--- a/src/mono/browser/runtime/profiler.ts
+++ b/src/mono/browser/runtime/profiler.ts
@@ -1,6 +1,8 @@
// 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 { ENVIRONMENT_IS_WEB, mono_assert, runtimeHelpers } from "./globals";
import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions, LogProfilerOptions } from "./types/internal";
import { profiler_c_functions as cwraps } from "./cwraps";
@@ -105,3 +107,24 @@ export function mono_wasm_profiler_leave (method: MonoMethod): void {
globalThis.performance.measure(methodName, options);
}
}
+
+/* eslint-disable @typescript-eslint/no-unused-vars */
+export function ds_rt_websocket_create (urlPtr :CharPtr):number {
+ throw new Error("TODO");
+}
+
+export function ds_rt_websocket_send (client_socket :number, buffer:VoidPtr, bytes_to_write:number):number {
+ throw new Error("TODO");
+}
+
+export function ds_rt_websocket_poll (client_socket :number):number {
+ throw new Error("TODO");
+}
+
+export function ds_rt_websocket_recv (client_socket :number, buffer:VoidPtr, bytes_to_read:number):number {
+ throw new Error("TODO");
+}
+
+export function ds_rt_websocket_close (client_socket :number):number {
+ throw new Error("TODO");
+}
diff --git a/src/mono/browser/runtime/rollup.config.js b/src/mono/browser/runtime/rollup.config.js
index e50d79fd09a93c..c1e3874c052766 100644
--- a/src/mono/browser/runtime/rollup.config.js
+++ b/src/mono/browser/runtime/rollup.config.js
@@ -20,6 +20,7 @@ const nativeBinDir = process.env.NativeBinDir ? process.env.NativeBinDir.replace
const wasmObjDir = process.env.WasmObjDir ? process.env.WasmObjDir.replace(/"/g, "") : "obj";
const wasmEnableThreads = process.env.WasmEnableThreads === "true" ? true : false;
const wasmEnableSIMD = process.env.WASM_ENABLE_SIMD === "1" ? true : false;
+const wasmEnablePerfTracing = process.env.FEATURE_PERFTRACING === "1" ? true : false;
const wasmEnableExceptionHandling = process.env.WASM_ENABLE_EH === "1" ? true : false;
const wasmEnableJsInteropByValue = process.env.ENABLE_JS_INTEROP_BY_VALUE == "1" ? true : false;
// because of stack walk at src/mono/browser/debugger/BrowserDebugProxy/MonoProxy.cs
@@ -108,6 +109,7 @@ const envConstants = {
configuration,
wasmEnableThreads,
wasmEnableSIMD,
+ wasmEnablePerfTracing,
wasmEnableExceptionHandling,
gitHash,
wasmEnableJsInteropByValue,
diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts
index 54f11256806273..2af269d1b226df 100644
--- a/src/mono/browser/runtime/types/internal.ts
+++ b/src/mono/browser/runtime/types/internal.ts
@@ -283,6 +283,7 @@ export type EmscriptenBuildOptions = {
enableAotProfiler: boolean,
enableBrowserProfiler: boolean,
enableLogProfiler: boolean,
+ enablePerfTracing: boolean,
runAOTCompilation: boolean,
wasmEnableThreads: boolean,
gitHash: string,
diff --git a/src/mono/mono.proj b/src/mono/mono.proj
index 5b133202f4076b..3b2b7fc88222ef 100644
--- a/src/mono/mono.proj
+++ b/src/mono/mono.proj
@@ -698,12 +698,18 @@ JS_ENGINES = [NODE_JS]
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/>
-
+
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS=1"/>
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/>
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_CONNECT_PORTS=1" />
+
+ <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_PAL_WS=1"/>
+ <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS=1"/>
+ <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/>
+
+
<_MonoCMakeArgs Include="-DSTATIC_COMPONENTS=1" />
diff --git a/src/mono/mono/component/CMakeLists.txt b/src/mono/mono/component/CMakeLists.txt
index 0e663e690fc4fd..53c0f1974ea836 100644
--- a/src/mono/mono/component/CMakeLists.txt
+++ b/src/mono/mono/component/CMakeLists.txt
@@ -216,6 +216,15 @@ endif()
if (ENABLE_PERFTRACING AND "${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}" IN_LIST components_to_build)
# Build EventPipe and DiagnosticServer with the Mono runtime implementation as unity-builds.
add_library(eventpipe-mono-objects OBJECT)
+
+ if (DISABLE_THREADS)
+ set_target_properties(
+ eventpipe-mono-objects
+ PROPERTIES
+ DISABLE_THREADS ON
+ )
+ endif()
+
set_target_properties(
eventpipe-mono-objects
PROPERTIES
diff --git a/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c
index 379b7bde8a1cae..05a762c6d362a4 100644
--- a/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c
+++ b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c
@@ -25,8 +25,14 @@ extern EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_
#define NUM_NANOSECONDS_IN_1_MS 1000000
// Sample profiler.
+#if defined(PERFTRACING_MULTI_THREADED)
static GArray * _sampled_thread_callstacks = NULL;
static uint32_t _max_sampled_thread_count = 32;
+#else
+MonoProfilerHandle _ep_rt_mono_sampling_profiler_provider;
+static EventPipeEvent *current_sampling_event = NULL;
+static ep_rt_thread_handle_t current_sampling_thread = NULL;
+#endif
// Mono profilers.
extern MonoProfilerHandle _ep_rt_mono_default_profiler_provider;
@@ -1207,6 +1213,7 @@ ep_rt_mono_walk_managed_stack_for_thread (
return true;
}
+#if defined(PERFTRACING_MULTI_THREADED)
bool
ep_rt_mono_sample_profiler_write_sampling_event_for_threads (
ep_rt_thread_handle_t sampling_thread,
@@ -1306,6 +1313,94 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads (
return true;
}
+#else // !PERFTRACING_MULTI_THREADED
+
+bool
+ep_rt_mono_sample_profiler_write_sampling_event_for_threads (
+ ep_rt_thread_handle_t sampling_thread,
+ EventPipeEvent *sampling_event)
+{
+ // here we just store the context for later
+ current_sampling_event = sampling_event;
+ current_sampling_thread = sampling_thread;
+
+ return true;
+}
+
+static void
+method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx)
+{
+ MonoThreadInfo *thread_info = mono_thread_info_current ();
+ SampleProfileStackWalkData stack_walk_data;
+ SampleProfileStackWalkData *data= &stack_walk_data;
+ THREAD_INFO_TYPE adapter = { { 0 } };
+
+ data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info));
+ data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&ctx->context);
+ data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR;
+ data->stack_walk_data.stack_contents = &data->stack_contents;
+ data->stack_walk_data.top_frame = true;
+ data->stack_walk_data.async_frame = false;
+ data->stack_walk_data.safe_point_frame = false;
+ data->stack_walk_data.runtime_invoke_frame = false;
+ ep_stack_contents_reset (&data->stack_contents);
+
+ mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (sample_profiler_walk_managed_stack_for_thread_callback, &ctx->context, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data);
+ if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) {
+ data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED;
+ }
+ if (data->stack_walk_data.top_frame && ep_stack_contents_get_length (&data->stack_contents) == 0) {
+ data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL;
+ }
+
+ if ((data->stack_walk_data.top_frame && data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL) || (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length (&data->stack_contents) > 0)) {
+ if (data->stack_walk_data.async_frame) {
+ for (uint32_t frame_count = 0; frame_count < data->stack_contents.next_available_frame; ++frame_count)
+ mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [frame_count], TRUE, FALSE);
+ }
+ mono_thread_info_set_tid (&adapter, ep_rt_uint64_t_to_thread_id_t (data->thread_id));
+ uint32_t payload_data = ep_rt_val_uint32_t (data->payload_data);
+ ep_write_sample_profile_event (current_sampling_thread, current_sampling_event, &adapter, &data->stack_contents, (uint8_t *)&payload_data, sizeof (payload_data));
+ }
+}
+
+static MonoProfilerCallInstrumentationFlags
+method_filter (MonoProfiler *prof, MonoMethod *method)
+{
+ // TODO add more instrumentation, something like MINT_SDB_SEQ_POINT
+ return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER;
+}
+
+#endif // PERFTRACING_MULTI_THREADED
+
+void
+ep_rt_mono_sampling_provider_component_init (void)
+{
+#if defined(PERFTRACING_SINGLE_THREADED)
+ // in single-threaded mode, we install instrumentation callbacks on the mono profiler, instead of stop-the-world
+ _ep_rt_mono_sampling_profiler_provider = mono_profiler_create (NULL);
+ // this has negative performance impact even when the EP client is not connected!
+ // but it has to be enabled before managed code starts running, because the instrumentation needs to be in place
+ mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_mono_sampling_profiler_provider, method_filter);
+#endif
+}
+
+void
+ep_rt_mono_sample_profiler_enabled (void)
+{
+#if defined(PERFTRACING_SINGLE_THREADED)
+ mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, method_enter);
+#endif
+}
+
+void
+ep_rt_mono_sample_profiler_disabled (void)
+{
+#if defined(PERFTRACING_SINGLE_THREADED)
+ mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, NULL);
+#endif
+}
+
void
ep_rt_mono_execute_rundown (dn_vector_ptr_t *execution_checkpoints)
{
diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c
index f07f5c6d263411..75851bc0503214 100644
--- a/src/mono/mono/eventpipe/ep-rt-mono.c
+++ b/src/mono/mono/eventpipe/ep-rt-mono.c
@@ -789,6 +789,7 @@ ep_rt_mono_component_init (void)
g_free (diag_env);
ep_rt_mono_runtime_provider_component_init ();
+ ep_rt_mono_sampling_provider_component_init ();
ep_rt_mono_profiler_provider_component_init ();
}
diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h
index 94ca5bb94f7e59..4baf1ff502443a 100644
--- a/src/mono/mono/eventpipe/ep-rt-mono.h
+++ b/src/mono/mono/eventpipe/ep-rt-mono.h
@@ -69,6 +69,8 @@ extern void ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *pro
extern void ep_rt_mono_init_providers_and_events (void);
extern bool ep_rt_mono_providers_validate_all_disabled (void);
extern bool ep_rt_mono_sample_profiler_write_sampling_event_for_threads (ep_rt_thread_handle_t sampling_thread, EventPipeEvent *sampling_event);
+extern void ep_rt_mono_sample_profiler_enabled (void);
+extern void ep_rt_mono_sample_profiler_disabled (void);
extern void ep_rt_mono_execute_rundown (dn_vector_ptr_t *execution_checkpoints);
extern int64_t ep_rt_mono_perf_counter_query (void);
extern int64_t ep_rt_mono_perf_frequency_query (void);
@@ -637,6 +639,22 @@ ep_rt_sample_profiler_write_sampling_event_for_threads (ep_rt_thread_handle_t sa
ep_rt_mono_sample_profiler_write_sampling_event_for_threads (sampling_thread, sampling_event);
}
+static
+inline
+void
+ep_rt_sample_profiler_enabled (void)
+{
+ ep_rt_mono_sample_profiler_enabled ();
+}
+
+static
+inline
+void
+ep_rt_sample_profiler_disabled (void)
+{
+ ep_rt_mono_sample_profiler_disabled ();
+}
+
static
void
ep_rt_notify_profiler_provider_created (EventPipeProvider *provider)
@@ -714,7 +732,11 @@ ep_rt_wait_event_wait (
{
//TODO, replace with low level PAL implementation.
EP_ASSERT (wait_event != NULL && wait_event->event != NULL);
+#if defined(PERFTRACING_MULTI_THREADED)
return (int32_t)mono_w32handle_wait_one (wait_event->event, timeout, alertable);
+#else
+ return (int32_t)0;
+#endif
}
static
@@ -830,6 +852,7 @@ typedef struct _rt_mono_thread_params_internal_t {
#undef EP_RT_DEFINE_THREAD_FUNC
#define EP_RT_DEFINE_THREAD_FUNC(name) static mono_thread_start_return_t WINAPI name (gpointer data)
+#if defined(PERFTRACING_MULTI_THREADED)
EP_RT_DEFINE_THREAD_FUNC (ep_rt_thread_mono_start_func)
{
rt_mono_thread_params_internal_t *thread_params = (rt_mono_thread_params_internal_t *)data;
@@ -845,6 +868,26 @@ EP_RT_DEFINE_THREAD_FUNC (ep_rt_thread_mono_start_func)
return result;
}
+#else // PERFTRACING_MULTI_THREADED
+// in single-threaded OS like browser, this is not wrapper for long running method
+// it's rather self sustaining callback, called from browser event loop
+EP_RT_DEFINE_THREAD_FUNC (ep_rt_thread_mono_start_func)
+{
+ rt_mono_thread_params_internal_t *thread_params = (rt_mono_thread_params_internal_t *)data;
+
+ mono_thread_start_return_t result = thread_params->thread_params.thread_func (thread_params);
+
+ if (result == 0) {
+ // self schedule again
+ mono_main_thread_schedule_ds_job ((ds_job_cb)ep_rt_thread_mono_start_func, (void*)thread_params);
+ }
+ else {
+ g_free (thread_params);
+ }
+
+ return result;
+}
+#endif // PERFTRACING_MULTI_THREADED
static
inline
@@ -861,7 +904,13 @@ ep_rt_thread_create (
thread_params->thread_params.thread_func = (ep_rt_thread_start_func)thread_func;
thread_params->thread_params.thread_params = params;
thread_params->background_thread = true;
+#if defined(PERFTRACING_MULTI_THREADED)
return (mono_thread_platform_create_thread (ep_rt_thread_mono_start_func, thread_params, NULL, (ep_rt_thread_id_t *)id) == TRUE) ? true : false;
+#else
+ // in single-threaded, it will run the callback inline and re-schedule itself if necessary
+ ep_rt_thread_mono_start_func (thread_params);
+ return true;
+#endif
}
return false;
@@ -880,6 +929,7 @@ inline
void
ep_rt_thread_sleep (uint64_t ns)
{
+#if defined(PERFTRACING_MULTI_THREADED)
MONO_REQ_GC_UNSAFE_MODE;
if (ns == 0) {
mono_thread_info_yield ();
@@ -888,6 +938,7 @@ ep_rt_thread_sleep (uint64_t ns)
g_usleep ((gulong)(ns / 1000));
MONO_EXIT_GC_SAFE;
}
+#endif
}
static
@@ -1970,6 +2021,8 @@ extern void ep_rt_mono_runtime_provider_fini (void);
extern void ep_rt_mono_runtime_provider_thread_started_callback (MonoProfiler *prof, uintptr_t tid);
extern void ep_rt_mono_runtime_provider_thread_stopped_callback (MonoProfiler *prof, uintptr_t tid);
+extern void ep_rt_mono_sampling_provider_component_init (void);
+
extern void ep_rt_mono_profiler_provider_component_init (void);
extern void ep_rt_mono_profiler_provider_init (void);
extern void ep_rt_mono_profiler_provider_fini (void);
diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c
index 4a9eb21b137adb..c9d5521a8b6708 100644
--- a/src/mono/mono/mini/mini-wasm.c
+++ b/src/mono/mono/mini/mini-wasm.c
@@ -453,6 +453,7 @@ G_BEGIN_DECLS
#ifdef DISABLE_THREADS
EMSCRIPTEN_KEEPALIVE void mono_wasm_execute_timer (void);
EMSCRIPTEN_KEEPALIVE void mono_background_exec (void);
+EMSCRIPTEN_KEEPALIVE void mono_wasm_ds_exec (void);
extern void mono_wasm_schedule_timer (int shortestDueTimeMs);
#else
extern void mono_target_thread_schedule_synchronization_context(MonoNativeThreadId target_thread);
diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c
index c1e6d9144e7b30..b0c01df8498f7e 100644
--- a/src/mono/mono/utils/mono-threads-wasm.c
+++ b/src/mono/mono/utils/mono-threads-wasm.c
@@ -322,6 +322,9 @@ extern void schedule_background_exec (void);
// when this is called from sgen it would be wrapper of sgen_perform_collection_inner
// when this is called from gc, it would be mono_runtime_do_background_work
#ifdef DISABLE_THREADS
+GSList *jobs;
+GSList *jobs_ds;
+
void
mono_main_thread_schedule_background_job (background_job_cb cb)
{
@@ -335,7 +338,20 @@ mono_main_thread_schedule_background_job (background_job_cb cb)
jobs = g_slist_prepend (jobs, (gpointer)cb);
}
-GSList *jobs;
+typedef struct {
+ ds_job_cb cb;
+ void* data;
+} DsJobRegistration;
+
+void
+mono_main_thread_schedule_ds_job (ds_job_cb cb, void* data)
+{
+ g_assert (cb);
+ DsJobRegistration* reg = g_new0 (DsJobRegistration, 1);
+ reg->cb = cb;
+ reg->data = data;
+ jobs_ds = g_slist_prepend (jobs_ds, (gpointer)reg);
+}
G_EXTERN_C
EMSCRIPTEN_KEEPALIVE void
@@ -356,6 +372,26 @@ mono_background_exec (void)
MONO_EXIT_GC_UNSAFE;
}
+G_EXTERN_C
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_ds_exec (void)
+{
+ MONO_ENTER_GC_UNSAFE;
+ GSList *j1 = jobs_ds, *cur1;
+ jobs_ds = NULL;
+
+ for (cur1 = j1; cur1; cur1 = cur1->next) {
+ DsJobRegistration* reg = (DsJobRegistration*)cur1->data;
+ g_assert (reg->cb);
+ THREADS_DEBUG ("mono_wasm_ds_exec running job %p \n", (gpointer)cb);
+ reg->cb (reg->data);
+ THREADS_DEBUG ("mono_wasm_ds_exec done job %p \n", (gpointer)cb);
+ g_free (reg);
+ }
+ g_slist_free (j1);
+ MONO_EXIT_GC_UNSAFE;
+}
+
#else /*DISABLE_THREADS*/
extern void mono_wasm_schedule_synchronization_context ();
diff --git a/src/mono/mono/utils/mono-threads-wasm.h b/src/mono/mono/utils/mono-threads-wasm.h
index 927c5b0eb0ea54..64fbd11a20ba5f 100644
--- a/src/mono/mono/utils/mono-threads-wasm.h
+++ b/src/mono/mono/utils/mono-threads-wasm.h
@@ -88,8 +88,8 @@ mono_wasm_atomic_wait_i32 (volatile int32_t *addr, int32_t expected, int32_t tim
}
#else /* DISABLE_THREADS */
-extern GSList *jobs;
void mono_background_exec (void);
+void mono_wasm_ds_exec (void);
#endif /* DISABLE_THREADS */
void
diff --git a/src/mono/mono/utils/mono-threads.h b/src/mono/mono/utils/mono-threads.h
index 8410e43ef9301a..1a8886ff333e3f 100644
--- a/src/mono/mono/utils/mono-threads.h
+++ b/src/mono/mono/utils/mono-threads.h
@@ -845,8 +845,10 @@ void mono_threads_join_unlock (void);
#ifdef HOST_WASM
typedef void (*background_job_cb)(void);
+typedef int (*ds_job_cb)(void* data);
#ifdef DISABLE_THREADS
void mono_main_thread_schedule_background_job (background_job_cb cb);
+void mono_main_thread_schedule_ds_job (ds_job_cb cb, void* data);
#else
void mono_target_thread_schedule_synchronization_context(MonoNativeThreadId target_thread);
#endif // DISABLE_THREADS
diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/WorkloadManifest.targets.in
index 39288d158b1181..3d9c3b75d162c8 100644
--- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/WorkloadManifest.targets.in
+++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/WorkloadManifest.targets.in
@@ -67,7 +67,6 @@
$(_MonoWorkloadRuntimePackPackageVersion)
Microsoft.NETCore.App.Runtime.Mono.multithread.**RID**
- Microsoft.NETCore.App.Runtime.Mono.perftrace.**RID**
diff --git a/src/native/eventpipe/CMakeLists.txt b/src/native/eventpipe/CMakeLists.txt
index 311293ca5f45cc..fe32de8f64661c 100644
--- a/src/native/eventpipe/CMakeLists.txt
+++ b/src/native/eventpipe/CMakeLists.txt
@@ -17,6 +17,15 @@ if (CLR_CMAKE_HOST_IOS OR CLR_CMAKE_HOST_TVOS OR CLR_CMAKE_HOST_ANDROID)
set(FEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT 1)
endif()
+# Use WebSocket for EventPipe on mobile browser
+if (CLR_CMAKE_HOST_BROWSER)
+ set(FEATURE_PERFTRACING_PAL_WS 1)
+ set(FEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT 1)
+ if (DISABLE_THREADS)
+ set(FEATURE_PERFTRACING_SINGLE_THREADED 1)
+ endif()
+endif()
+
configure_file(${CLR_SRC_NATIVE_DIR}/eventpipe/ep-shared-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/ep-shared-config.h)
# Define the DiagnosticsServer and EventPipe as interface libraries.
@@ -44,7 +53,11 @@ add_library(dn-diagnosticserver-pal INTERFACE)
target_include_directories(dn-diagnosticserver-pal INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(dn-diagnosticserver-pal INTERFACE dn-containers)
-if (FEATURE_PERFTRACING_PAL_TCP)
+if (FEATURE_PERFTRACING_PAL_WS)
+ target_sources(dn-diagnosticserver-pal INTERFACE
+ ds-ipc-pal-websocket.c
+ )
+elseif (FEATURE_PERFTRACING_PAL_TCP)
target_sources(dn-diagnosticserver-pal INTERFACE
ds-ipc-pal-socket.c
)
@@ -56,7 +69,7 @@ else()
target_sources(dn-diagnosticserver-pal INTERFACE
ds-ipc-pal-socket.c
)
-endif (FEATURE_PERFTRACING_PAL_TCP)
+endif (FEATURE_PERFTRACING_PAL_WS)
add_library(dn-eventpipe INTERFACE)
diff --git a/src/native/eventpipe/ds-ipc-pal-websocket.c b/src/native/eventpipe/ds-ipc-pal-websocket.c
new file mode 100644
index 00000000000000..0fe5c7d61aa2b7
--- /dev/null
+++ b/src/native/eventpipe/ds-ipc-pal-websocket.c
@@ -0,0 +1,556 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifdef FEATURE_PERFTRACING_STANDALONE_PAL
+#define EP_NO_RT_DEPENDENCY
+#endif
+
+#include "ds-rt-config.h"
+
+#ifdef ENABLE_PERFTRACING
+
+#define DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER
+#include "ds-ipc-pal-websocket.h"
+
+#define DS_IPC_INVALID_SOCKET -1
+
+#ifndef EP_NO_RT_DEPENDENCY
+#include "ds-rt.h"
+#else
+#ifdef FEATURE_CORECLR
+#include
+#include "processdescriptor.h"
+#endif
+
+#ifndef ep_return_null_if_nok
+#define ep_return_null_if_nok(expr) do { if (!(expr)) return NULL; } while (0)
+#endif
+
+#ifndef ep_raise_error_if_nok
+#define ep_raise_error_if_nok(expr) do { if (!(expr)) goto ep_on_error; } while (0)
+#endif
+
+#ifndef ep_raise_error
+#define ep_raise_error() do { goto ep_on_error; } while (0)
+#endif
+
+#ifndef ep_exit_error_handler
+#define ep_exit_error_handler() do { goto ep_on_exit; } while (0)
+#endif
+
+#ifndef EP_ASSERT
+#define EP_ASSERT assert
+#endif
+
+#ifndef DS_ENTER_BLOCKING_PAL_SECTION
+#define DS_ENTER_BLOCKING_PAL_SECTION
+#endif
+
+#ifndef DS_EXIT_BLOCKING_PAL_SECTION
+#define DS_EXIT_BLOCKING_PAL_SECTION
+#endif
+
+#undef ep_rt_object_alloc
+#define ep_rt_object_alloc(obj_type) ((obj_type *)calloc(1, sizeof(obj_type)))
+
+static
+inline
+void
+ep_rt_object_free (void *ptr)
+{
+ if (ptr)
+ free (ptr);
+}
+
+#undef ep_rt_object_array_alloc
+#define ep_rt_object_array_alloc(obj_type,size) ((obj_type *)calloc (size, sizeof(obj_type)))
+
+static
+inline
+void
+ep_rt_object_array_free (void *ptr)
+{
+ if (ptr)
+ free (ptr);
+}
+#endif
+
+static bool _ipc_pal_socket_init = false;
+
+/*
+ * Forward declares of all static functions.
+ */
+
+static
+bool
+ipc_socket_recv (
+ ds_ipc_websocket_t s,
+ uint8_t * buffer,
+ ssize_t bytes_to_read,
+ ssize_t *bytes_read);
+
+static
+bool
+ipc_socket_send (
+ ds_ipc_websocket_t s,
+ const uint8_t *buffer,
+ ssize_t bytes_to_write,
+ ssize_t *bytes_written);
+
+static
+bool
+ipc_transport_get_default_name (
+ ep_char8_t *name,
+ int32_t name_len);
+
+static
+bool
+ipc_init_listener (
+ DiagnosticsIpc *ipc,
+ ds_ipc_error_callback_func callback);
+
+static
+DiagnosticsIpc *
+ipc_alloc_ws_address (
+ DiagnosticsIpc *ipc,
+ DiagnosticsIpcConnectionMode mode,
+ const ep_char8_t *ipc_name);
+
+static
+void
+ipc_stream_free_func (void *object);
+
+static
+bool
+ipc_stream_read_func (
+ void *object,
+ uint8_t *buffer,
+ uint32_t bytes_to_read,
+ uint32_t *bytes_read,
+ uint32_t timeout_ms);
+
+static
+bool
+ipc_stream_write_func (
+ void *object,
+ const uint8_t *buffer,
+ uint32_t bytes_to_write,
+ uint32_t *bytes_written,
+ uint32_t timeout_ms);
+
+static
+bool
+ipc_stream_flush_func (void *object);
+
+static
+bool
+ipc_stream_close_func (void *object);
+
+static
+DiagnosticsIpcStream *
+ipc_stream_alloc (
+ int client_socket);
+/*
+ * Implementation
+ */
+
+
+static
+DiagnosticsIpc *
+ipc_alloc_ws_address (
+ DiagnosticsIpc *ipc,
+ DiagnosticsIpcConnectionMode mode,
+ const ep_char8_t *ipc_name)
+{
+ EP_ASSERT (ipc != NULL);
+ ep_return_null_if_nok (ipc_name != NULL);
+
+ ipc->server_url = ep_rt_utf8_string_dup (ipc_name);
+
+ ep_raise_error_if_nok (ipc->server_url != NULL);
+
+ep_on_exit:
+ return ipc;
+
+ep_on_error:
+ ipc = NULL;
+ ep_exit_error_handler ();
+}
+
+
+/*
+ * DiagnosticsIpc Socket PAL.
+ */
+
+bool
+ds_ipc_pal_init (void)
+{
+ return true;
+}
+
+bool
+ds_ipc_pal_shutdown (void)
+{
+ return true;
+}
+
+DiagnosticsIpc *
+ds_ipc_alloc (
+ const ep_char8_t *ipc_name,
+ DiagnosticsIpcConnectionMode mode,
+ ds_ipc_error_callback_func callback)
+{
+ DiagnosticsIpc *instance = NULL;
+
+ instance = ep_rt_object_alloc (DiagnosticsIpc);
+ ep_raise_error_if_nok (instance != NULL);
+
+ instance->server_socket = DS_IPC_INVALID_SOCKET;
+ instance->is_closed = false;
+
+ ep_raise_error_if_nok (ipc_alloc_ws_address (instance, mode, ipc_name) != NULL);
+
+ep_on_exit:
+ return instance;
+
+ep_on_error:
+ ds_ipc_free (instance);
+ instance = NULL;
+ ep_exit_error_handler ();
+}
+
+void
+ds_ipc_free (DiagnosticsIpc *ipc)
+{
+ if (!ipc)
+ return;
+
+ ds_ipc_close (ipc, false, NULL);
+
+ ep_rt_object_free (ipc);
+}
+
+void
+ds_ipc_reset (DiagnosticsIpc *ipc)
+{
+}
+
+int32_t
+ds_ipc_poll (
+ DiagnosticsIpcPollHandle *poll_handles_data,
+ size_t poll_handles_data_len,
+ uint32_t timeout_ms,
+ ds_ipc_error_callback_func callback)
+{
+ for (uint32_t i = 0; i < poll_handles_data_len; ++i) {
+ // CLIENT
+ EP_ASSERT (poll_handles_data [i].stream != NULL);
+ int client_socket = poll_handles_data [i].stream->client_socket;
+ int pending = ds_rt_websocket_poll (client_socket);
+ if (pending < 0){
+ poll_handles_data [i].events = (uint8_t)DS_IPC_POLL_EVENTS_ERR;
+ return 1;
+ }
+ if (pending > 0){
+ poll_handles_data [i].events = (uint8_t)DS_IPC_POLL_EVENTS_SIGNALED;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+bool
+ds_ipc_listen (
+ DiagnosticsIpc *ipc,
+ ds_ipc_error_callback_func callback)
+{
+ // NOT SUPPORTED
+ return false;
+}
+
+DiagnosticsIpcStream *
+ds_ipc_accept (
+ DiagnosticsIpc *ipc,
+ ds_ipc_error_callback_func callback)
+{
+ // NOT SUPPORTED
+ return NULL;
+}
+
+DiagnosticsIpcStream *
+ds_ipc_connect (
+ DiagnosticsIpc *ipc,
+ uint32_t timeout_ms,
+ ds_ipc_error_callback_func callback,
+ bool *timed_out)
+{
+ EP_ASSERT (ipc != NULL);
+ EP_ASSERT (timed_out != NULL);
+
+
+ DiagnosticsIpcStream *stream = NULL;
+
+ bool success = false;
+ *timed_out = false;
+ int client_socket = ds_rt_websocket_create (ipc->server_url);
+
+ success = client_socket > 0;
+ ep_raise_error_if_nok (success == true);
+
+ stream = ipc_stream_alloc ((ds_ipc_websocket_t)client_socket);
+
+ep_on_exit:
+ return stream;
+
+ep_on_error:
+ ds_ipc_stream_free (stream);
+ stream = NULL;
+
+ ep_exit_error_handler ();
+}
+
+void
+ds_ipc_close (
+ DiagnosticsIpc *ipc,
+ bool is_shutdown,
+ ds_ipc_error_callback_func callback)
+{
+ EP_ASSERT (ipc != NULL);
+ if (ipc->is_closed)
+ return;
+
+ ipc->is_closed = true;
+}
+
+int32_t
+ds_ipc_to_string (
+ DiagnosticsIpc *ipc,
+ ep_char8_t *buffer,
+ uint32_t buffer_len)
+{
+ EP_ASSERT (ipc != NULL);
+ EP_ASSERT (buffer != NULL);
+ EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN);
+
+ int32_t result = snprintf (buffer, buffer_len, "{ server_socket = %d }", (int32_t)(size_t)ipc->server_socket);
+ return (result > 0 && result < (int32_t)buffer_len) ? result : 0;
+}
+
+/*
+ * DiagnosticsIpcStream.
+ */
+
+static
+void
+ipc_stream_free_func (void *object)
+{
+ EP_ASSERT (object != NULL);
+ DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object;
+ ds_ipc_stream_free (ipc_stream);
+}
+
+static
+bool
+ipc_stream_read_func (
+ void *object,
+ uint8_t *buffer,
+ uint32_t bytes_to_read,
+ uint32_t *bytes_read,
+ uint32_t timeout_ms)
+{
+ EP_ASSERT (object != NULL);
+ EP_ASSERT (buffer != NULL);
+ EP_ASSERT (bytes_read != NULL);
+
+ bool success = false;
+ DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object;
+ int total_bytes_read = ds_rt_websocket_recv (ipc_stream->client_socket, buffer, bytes_to_read);
+
+ success = total_bytes_read >= 0;
+ ep_raise_error_if_nok (success == true);
+
+ep_on_exit:
+ *bytes_read = (uint32_t)total_bytes_read;
+ return success;
+
+ep_on_error:
+ total_bytes_read = 0;
+ success = false;
+ ep_exit_error_handler ();
+}
+
+static
+bool
+ipc_stream_write_func (
+ void *object,
+ const uint8_t *buffer,
+ uint32_t bytes_to_write,
+ uint32_t *bytes_written,
+ uint32_t timeout_ms)
+{
+ EP_ASSERT (object != NULL);
+ EP_ASSERT (buffer != NULL);
+ EP_ASSERT (bytes_written != NULL);
+
+ bool success = false;
+ DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object;
+
+ int total_bytes_written = ds_rt_websocket_send (ipc_stream->client_socket, buffer, bytes_to_write);
+ success = total_bytes_written >= 0;
+
+ ep_raise_error_if_nok (success == true);
+
+ep_on_exit:
+ *bytes_written = (uint32_t)total_bytes_written;
+ return success;
+
+ep_on_error:
+ total_bytes_written = 0;
+ success = false;
+ ep_exit_error_handler ();
+}
+
+static
+bool
+ipc_stream_flush_func (void *object)
+{
+ return true;
+}
+
+static
+bool
+ipc_stream_close_func (void *object)
+{
+ EP_ASSERT (object != NULL);
+ DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object;
+ return ds_ipc_stream_close (ipc_stream, NULL);
+}
+
+static IpcStreamVtable ipc_stream_vtable = {
+ ipc_stream_free_func,
+ ipc_stream_read_func,
+ ipc_stream_write_func,
+ ipc_stream_flush_func,
+ ipc_stream_close_func };
+
+static
+DiagnosticsIpcStream *
+ipc_stream_alloc (
+ int client_socket)
+{
+ DiagnosticsIpcStream *instance = ep_rt_object_alloc (DiagnosticsIpcStream);
+ ep_raise_error_if_nok (instance != NULL);
+
+ instance->stream.vtable = &ipc_stream_vtable;
+ instance->client_socket = client_socket;
+
+ep_on_exit:
+ return instance;
+
+ep_on_error:
+ ds_ipc_stream_free (instance);
+ instance = NULL;
+ ep_exit_error_handler ();
+}
+
+IpcStream *
+ds_ipc_stream_get_stream_ref (DiagnosticsIpcStream *ipc_stream)
+{
+ return &ipc_stream->stream;
+}
+
+int32_t
+ds_ipc_stream_get_handle_int32_t (DiagnosticsIpcStream *ipc_stream)
+{
+ return (int32_t)ipc_stream->client_socket;
+}
+
+void
+ds_ipc_stream_free (DiagnosticsIpcStream *ipc_stream)
+{
+ if(!ipc_stream)
+ return;
+
+ ds_ipc_stream_close (ipc_stream, NULL);
+ ep_rt_object_free (ipc_stream);
+}
+
+bool
+ds_ipc_stream_read (
+ DiagnosticsIpcStream *ipc_stream,
+ uint8_t *buffer,
+ uint32_t bytes_to_read,
+ uint32_t *bytes_read,
+ uint32_t timeout_ms)
+{
+ return ipc_stream_read_func (
+ ipc_stream,
+ buffer,
+ bytes_to_read,
+ bytes_read,
+ timeout_ms);
+}
+
+bool
+ds_ipc_stream_write (
+ DiagnosticsIpcStream *ipc_stream,
+ const uint8_t *buffer,
+ uint32_t bytes_to_write,
+ uint32_t *bytes_written,
+ uint32_t timeout_ms)
+{
+ return ipc_stream_write_func (
+ ipc_stream,
+ buffer,
+ bytes_to_write,
+ bytes_written,
+ timeout_ms);
+}
+
+bool
+ds_ipc_stream_flush (DiagnosticsIpcStream *ipc_stream)
+{
+ return ipc_stream_flush_func (ipc_stream);
+}
+
+bool
+ds_ipc_stream_close (
+ DiagnosticsIpcStream *ipc_stream,
+ ds_ipc_error_callback_func callback)
+{
+ EP_ASSERT (ipc_stream != NULL);
+
+ if (ipc_stream->client_socket != DS_IPC_INVALID_SOCKET) {
+ ds_ipc_stream_flush (ipc_stream);
+
+ int res = ds_rt_websocket_close (ipc_stream->client_socket);
+
+ ipc_stream->client_socket = DS_IPC_INVALID_SOCKET;
+
+ return res == 0;
+ }
+
+ return true;
+}
+
+int32_t
+ds_ipc_stream_to_string (
+ DiagnosticsIpcStream *ipc_stream,
+ ep_char8_t *buffer,
+ uint32_t buffer_len)
+{
+ EP_ASSERT (ipc_stream != NULL);
+ EP_ASSERT (buffer != NULL);
+ EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN);
+
+ int32_t result = snprintf (buffer, buffer_len, "{ client_socket = %d }", (int32_t)(size_t)ipc_stream->client_socket);
+ return (result > 0 && result < (int32_t)buffer_len) ? result : 0;
+}
+
+#endif /* ENABLE_PERFTRACING */
+
+#ifndef DS_INCLUDE_SOURCE_FILES
+extern const char quiet_linker_empty_file_warning_diagnostics_ipc_pal_socket;
+const char quiet_linker_empty_file_warning_diagnostics_ipc_pal_socket = 0;
+#endif
diff --git a/src/native/eventpipe/ds-ipc-pal-websocket.h b/src/native/eventpipe/ds-ipc-pal-websocket.h
new file mode 100644
index 00000000000000..1273d9367c9d41
--- /dev/null
+++ b/src/native/eventpipe/ds-ipc-pal-websocket.h
@@ -0,0 +1,63 @@
+#ifndef __DIAGNOSTICS_IPC_PAL_WEB_SOCKET_H__
+#define __DIAGNOSTICS_IPC_PAL_WEB_SOCKET_H__
+
+#include "ds-rt-config.h"
+
+#ifdef ENABLE_PERFTRACING
+#include "ds-ipc-pal.h"
+
+#undef DS_IMPL_GETTER_SETTER
+#ifdef DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER
+#define DS_IMPL_GETTER_SETTER
+#endif
+#include "ds-getter-setter.h"
+
+typedef int ds_ipc_websocket_t;
+
+/*
+ * DiagnosticsIpc.
+ */
+
+#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER)
+struct _DiagnosticsIpc {
+#else
+struct _DiagnosticsIpc_Internal {
+#endif
+ ep_char8_t *server_url;
+ ds_ipc_websocket_t server_socket;
+ bool is_closed;
+};
+
+#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER)
+struct _DiagnosticsIpc {
+ uint8_t _internal [sizeof (struct _DiagnosticsIpc_Internal)];
+};
+#endif
+
+/*
+ * DiagnosticsIpcStream.
+ */
+
+#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER)
+struct _DiagnosticsIpcStream {
+#else
+struct _DiagnosticsIpcStream_Internal {
+#endif
+ IpcStream stream;
+ ds_ipc_websocket_t client_socket;
+};
+
+#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_PAL_SOCKET_GETTER_SETTER)
+struct _DiagnosticsIpcStream {
+ uint8_t _internal [sizeof (struct _DiagnosticsIpcStream_Internal)];
+};
+#endif
+
+extern int ds_rt_websocket_poll (int client_socket);
+extern int ds_rt_websocket_create (const char* url);
+extern int ds_rt_websocket_recv (int client_socket, const uint8_t* buffer, uint32_t bytes_to_read);
+extern int ds_rt_websocket_send (int client_socket, const uint8_t* buffer, uint32_t bytes_to_write);
+extern int ds_rt_websocket_close(int client_socket);
+
+#endif /* ENABLE_PERFTRACING */
+#endif /* __DIAGNOSTICS_IPC_PAL_WEB_SOCKET_H__ */
diff --git a/src/native/eventpipe/ds-ipc.c b/src/native/eventpipe/ds-ipc.c
index 7d3c56ee28d452..f172f6b9b96788 100644
--- a/src/native/eventpipe/ds-ipc.c
+++ b/src/native/eventpipe/ds-ipc.c
@@ -371,7 +371,7 @@ ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func call
dn_vector_t ipc_poll_handles;
ep_raise_error_if_nok (dn_vector_custom_init_t (&ipc_poll_handles, ¶ms, DiagnosticsIpcPollHandle));
- while (!stream) {
+ do {
connect_success = true;
DN_VECTOR_PTR_FOREACH_BEGIN (DiagnosticsPort *, port, _ds_port_array) {
DiagnosticsIpcPollHandle ipc_poll_handle;
@@ -455,6 +455,13 @@ ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func call
// clear the view.
dn_vector_clear (&ipc_poll_handles);
}
+#if defined(PERFTRACING_MULTI_THREADED)
+ while (!stream);
+#else
+ // in single-threaded mode, we only do one poll
+ // we can't loop here, that would block the browser event loop
+ while (false);
+#endif
ep_on_exit:
DS_LOG_DEBUG_2 ("ds_ipc_stream_factory_get_next_available_stream - EXIT :: Poll attempt: %d, stream using handle %d.", poll_attempts, stream ? ds_ipc_stream_get_handle_int32_t (stream) : -1);
diff --git a/src/native/eventpipe/ds-rt-config.h b/src/native/eventpipe/ds-rt-config.h
index 89237313362fb8..f3feaa4211275d 100644
--- a/src/native/eventpipe/ds-rt-config.h
+++ b/src/native/eventpipe/ds-rt-config.h
@@ -11,6 +11,9 @@
#define DS_INCLUDE_SOURCE_FILES
#endif
+#ifdef ENABLE_PERFTRACING_PAL_WS
+#define DS_IPC_PAL_WS
+#else
#ifdef ENABLE_PERFTRACING_PAL_TCP
#define DS_IPC_PAL_TCP
#else
@@ -20,6 +23,7 @@
#define DS_IPC_PAL_NAMEDPIPES
#endif
#endif
+#endif
#ifdef DISABLE_PERFTRACING_CONNECT_PORTS
#define DS_IPC_DISABLE_CONNECT_PORTS
diff --git a/src/native/eventpipe/ds-server.c b/src/native/eventpipe/ds-server.c
index b3cf9f3b27e032..348dd1ad81a8dd 100644
--- a/src/native/eventpipe/ds-server.c
+++ b/src/native/eventpipe/ds-server.c
@@ -112,6 +112,61 @@ server_warning_callback (
DS_LOG_WARNING_2 ("warning (%d): %s.", code, message);
}
+static size_t server_loop_tick (void) {
+ if (server_volatile_load_shutting_down_state ())
+ return 1; // done
+ DiagnosticsIpcStream *stream = ds_ipc_stream_factory_get_next_available_stream (server_warning_callback);
+ if (!stream)
+ return 0; // continue
+
+ ds_rt_auto_trace_signal ();
+
+ DiagnosticsIpcMessage message;
+ if (!ds_ipc_message_init (&message))
+ return 0; // continue
+
+ if (!ds_ipc_message_initialize_stream (&message, stream)) {
+ ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING);
+ ds_ipc_stream_free (stream);
+ ds_ipc_message_fini (&message);
+ return 0; // continue
+ }
+
+ if (ep_rt_utf8_string_compare (
+ (const ep_char8_t *)ds_ipc_header_get_magic_ref (ds_ipc_message_get_header_ref (&message)),
+ (const ep_char8_t *)DOTNET_IPC_V1_MAGIC) != 0) {
+
+ ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_MAGIC);
+ ds_ipc_stream_free (stream);
+ ds_ipc_message_fini (&message);
+ return 0; // continue
+ }
+
+ DS_LOG_INFO_2 ("DiagnosticServer - received IPC message with command set (%d) and command id (%d)", ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message)), ds_ipc_header_get_commandid (ds_ipc_message_get_header_ref (&message)));
+
+ switch ((DiagnosticsServerCommandSet)ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message))) {
+ case DS_SERVER_COMMANDSET_EVENTPIPE:
+ ds_eventpipe_protocol_helper_handle_ipc_message (&message, stream);
+ break;
+ case DS_SERVER_COMMANDSET_DUMP:
+ ds_dump_protocol_helper_handle_ipc_message (&message, stream);
+ break;
+ case DS_SERVER_COMMANDSET_PROCESS:
+ ds_process_protocol_helper_handle_ipc_message (&message, stream);
+ break;
+ case DS_SERVER_COMMANDSET_PROFILER:
+ ds_profiler_protocol_helper_handle_ipc_message (&message, stream);
+ break;
+ default:
+ server_protocol_helper_unknown_command (&message, stream);
+ break;
+ }
+
+ ds_ipc_message_fini (&message);
+
+ return 0; // continue
+}
+
EP_RT_DEFINE_THREAD_FUNC (server_thread)
{
EP_ASSERT (server_volatile_load_shutting_down_state () || ds_ipc_stream_factory_has_active_ports ());
@@ -125,58 +180,12 @@ EP_RT_DEFINE_THREAD_FUNC (server_thread)
return 1;
}
- while (!server_volatile_load_shutting_down_state ()) {
- DiagnosticsIpcStream *stream = ds_ipc_stream_factory_get_next_available_stream (server_warning_callback);
- if (!stream)
- continue;
-
- ds_rt_auto_trace_signal ();
-
- DiagnosticsIpcMessage message;
- if (!ds_ipc_message_init (&message))
- continue;
-
- if (!ds_ipc_message_initialize_stream (&message, stream)) {
- ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING);
- ds_ipc_stream_free (stream);
- ds_ipc_message_fini (&message);
- continue;
- }
-
- if (ep_rt_utf8_string_compare (
- (const ep_char8_t *)ds_ipc_header_get_magic_ref (ds_ipc_message_get_header_ref (&message)),
- (const ep_char8_t *)DOTNET_IPC_V1_MAGIC) != 0) {
-
- ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_MAGIC);
- ds_ipc_stream_free (stream);
- ds_ipc_message_fini (&message);
- continue;
- }
-
- DS_LOG_INFO_2 ("DiagnosticServer - received IPC message with command set (%d) and command id (%d)", ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message)), ds_ipc_header_get_commandid (ds_ipc_message_get_header_ref (&message)));
-
- switch ((DiagnosticsServerCommandSet)ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message))) {
- case DS_SERVER_COMMANDSET_EVENTPIPE:
- ds_eventpipe_protocol_helper_handle_ipc_message (&message, stream);
- break;
- case DS_SERVER_COMMANDSET_DUMP:
- ds_dump_protocol_helper_handle_ipc_message (&message, stream);
- break;
- case DS_SERVER_COMMANDSET_PROCESS:
- ds_process_protocol_helper_handle_ipc_message (&message, stream);
- break;
- case DS_SERVER_COMMANDSET_PROFILER:
- ds_profiler_protocol_helper_handle_ipc_message (&message, stream);
- break;
- default:
- server_protocol_helper_unknown_command (&message, stream);
- break;
- }
-
- ds_ipc_message_fini (&message);
- }
-
+#if defined(PERFTRACING_MULTI_THREADED)
+ while (server_loop_tick () == 0) { }
return (ep_rt_thread_start_func_return_t)0;
+#else // !PERFTRACING_MULTI_THREADED
+ return (ep_rt_thread_start_func_return_t)server_loop_tick ();
+#endif // PERFTRACING_MULTI_THREADED
}
void
@@ -258,6 +267,8 @@ ds_server_shutdown (void)
void
ds_server_pause_for_diagnostics_monitor (void)
{
+// pause is not implemented for single-threaded
+#if defined(PERFTRACING_MULTI_THREADED)
_is_paused_for_startup = true;
if (ds_ipc_stream_factory_any_suspended_ports ()) {
@@ -272,6 +283,7 @@ ds_server_pause_for_diagnostics_monitor (void)
}
// allow wait failures to fall through and the runtime to continue coming up
+#endif
}
void
diff --git a/src/native/eventpipe/ep-rt.h b/src/native/eventpipe/ep-rt.h
index 060bb71503befc..80a84387c8ef21 100644
--- a/src/native/eventpipe/ep-rt.h
+++ b/src/native/eventpipe/ep-rt.h
@@ -204,6 +204,14 @@ static
void
ep_rt_sample_profiler_write_sampling_event_for_threads (ep_rt_thread_handle_t sampling_thread, EventPipeEvent *sampling_event);
+static
+void
+ep_rt_sample_profiler_enabled (void);
+
+static
+void
+ep_rt_sample_profiler_disabled (void);
+
static
void
ep_rt_notify_profiler_provider_created (EventPipeProvider *provider);
diff --git a/src/native/eventpipe/ep-sample-profiler.c b/src/native/eventpipe/ep-sample-profiler.c
index b1c10d76a9deb3..38f2e2eece56f4 100644
--- a/src/native/eventpipe/ep-sample-profiler.c
+++ b/src/native/eventpipe/ep-sample-profiler.c
@@ -84,6 +84,7 @@ sample_profiler_store_can_start_sampling (bool start_sampling)
ep_rt_volatile_store_uint32_t (&_can_start_sampling, start_sampling ? 1 : 0);
}
+#if defined(PERFTRACING_MULTI_THREADED)
EP_RT_DEFINE_THREAD_FUNC (sampling_thread)
{
EP_ASSERT (data != NULL);
@@ -108,6 +109,7 @@ EP_RT_DEFINE_THREAD_FUNC (sampling_thread)
return (ep_rt_thread_start_func_return_t)0;
}
+#endif
static
void
@@ -192,6 +194,9 @@ sample_profiler_enable (void)
if (!sample_profiler_load_profiling_enabled ()) {
sample_profiler_store_profiling_enabled (true);
+ ep_rt_sample_profiler_enabled ();
+
+#if defined(PERFTRACING_MULTI_THREADED)
EP_ASSERT (!ep_rt_wait_event_is_valid (&_thread_shutdown_event));
ep_rt_wait_event_alloc (&_thread_shutdown_event, true, false);
if (!ep_rt_wait_event_is_valid (&_thread_shutdown_event))
@@ -200,6 +205,10 @@ sample_profiler_enable (void)
ep_rt_thread_id_t thread_id = ep_rt_uint64_t_to_thread_id_t (0);
if (!ep_rt_thread_create ((void *)sampling_thread, NULL, EP_THREAD_TYPE_SAMPLING, &thread_id))
EP_UNREACHABLE ("Unable to create sample profiler thread.");
+#else
+ // once
+ ep_rt_sample_profiler_write_sampling_event_for_threads (ep_rt_thread_get_handle (), _thread_time_event);
+#endif
sample_profiler_set_time_granularity ();
}
@@ -288,9 +297,13 @@ ep_sample_profiler_disable (void)
// when profiling is disabled.
sample_profiler_store_profiling_enabled (false);
+ ep_rt_sample_profiler_disabled ();
+
// Wait for the sampling thread to clean itself up.
+#if defined(PERFTRACING_MULTI_THREADED)
ep_rt_wait_event_wait (&_thread_shutdown_event, EP_INFINITE_WAIT, false);
ep_rt_wait_event_free (&_thread_shutdown_event);
+#endif
if (_time_period_is_set)
sample_profiler_reset_time_granularity ();
diff --git a/src/native/eventpipe/ep-session.c b/src/native/eventpipe/ep-session.c
index c79d3972cd5587..406c207f5ba829 100644
--- a/src/native/eventpipe/ep-session.c
+++ b/src/native/eventpipe/ep-session.c
@@ -34,6 +34,19 @@ ep_session_remove_dangling_session_states (EventPipeSession *session);
* EventPipeSession.
*/
+static size_t streaming_loop_tick(EventPipeSession *const session, bool *events_written) {
+ if (!ep_session_get_streaming_enabled (session)){
+ session->streaming_thread = NULL;
+ return 1; // done
+ }
+ if (!ep_session_write_all_buffers_to_file (session, events_written)) {
+ session->streaming_thread = NULL;
+ ep_disable ((EventPipeSessionID)session);
+ return 1; // done
+ }
+ return 0; // try again
+}
+
EP_RT_DEFINE_THREAD_FUNC (streaming_thread)
{
EP_ASSERT (data != NULL);
@@ -46,24 +59,21 @@ EP_RT_DEFINE_THREAD_FUNC (streaming_thread)
if (session->session_type != EP_SESSION_TYPE_IPCSTREAM && session->session_type != EP_SESSION_TYPE_FILESTREAM)
return 1;
+#if defined(PERFTRACING_MULTI_THREADED)
if (!thread_params->thread || !ep_rt_thread_has_started (thread_params->thread))
return 1;
+#endif
+ bool events_written = false;
session->streaming_thread = thread_params->thread;
- bool success = true;
- ep_rt_wait_event_handle_t *wait_event = ep_session_get_wait_event (session);
-
ep_rt_volatile_store_uint32_t (&session->started, 1);
- EP_GCX_PREEMP_ENTER
- while (ep_session_get_streaming_enabled (session)) {
- bool events_written = false;
- if (!ep_session_write_all_buffers_to_file (session, &events_written)) {
- success = false;
- break;
- }
+#if defined(PERFTRACING_MULTI_THREADED)
+ ep_rt_wait_event_handle_t *wait_event = ep_session_get_wait_event (session);
+ EP_GCX_PREEMP_ENTER
+ while (streaming_loop_tick(session, &events_written) == 0) {
if (!events_written) {
// No events were available, sleep until more are available
ep_rt_wait_event_wait (wait_event, EP_INFINITE_WAIT, false);
@@ -72,16 +82,15 @@ EP_RT_DEFINE_THREAD_FUNC (streaming_thread)
// Wait until it's time to sample again.
const uint32_t timeout_ns = 100000000; // 100 msec.
ep_rt_thread_sleep (timeout_ns);
- }
- session->streaming_thread = NULL;
+ events_written = false;
+ }
ep_rt_wait_event_set (&session->rt_thread_shutdown_event);
EP_GCX_PREEMP_EXIT
-
- if (!success)
- ep_disable ((EventPipeSessionID)session);
-
return (ep_rt_thread_start_func_return_t)0;
+#else // !PERFTRACING_MULTI_THREADED
+ return (ep_rt_thread_start_func_return_t) streaming_loop_tick(session, &events_written);
+#endif // PERFTRACING_MULTI_THREADED
}
static
diff --git a/src/native/eventpipe/ep-shared-config.h.in b/src/native/eventpipe/ep-shared-config.h.in
index 5b60e03427603e..7748da024ecf6b 100644
--- a/src/native/eventpipe/ep-shared-config.h.in
+++ b/src/native/eventpipe/ep-shared-config.h.in
@@ -10,6 +10,18 @@
#define ENABLE_PERFTRACING_PAL_TCP
#endif
+#cmakedefine FEATURE_PERFTRACING_PAL_WS
+#ifdef FEATURE_PERFTRACING_PAL_WS
+#define ENABLE_PERFTRACING_PAL_WS
+#endif
+
+#cmakedefine FEATURE_PERFTRACING_SINGLE_THREADED
+#ifdef FEATURE_PERFTRACING_SINGLE_THREADED
+#define PERFTRACING_SINGLE_THREADED
+#else
+#define PERFTRACING_MULTI_THREADED
+#endif
+
#cmakedefine FEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS
#ifdef FEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS
#define DISABLE_PERFTRACING_LISTEN_PORTS
diff --git a/src/native/libs/System.Native/pal_time.c b/src/native/libs/System.Native/pal_time.c
index a249fe653be1c0..488c49776fd9f3 100644
--- a/src/native/libs/System.Native/pal_time.c
+++ b/src/native/libs/System.Native/pal_time.c
@@ -121,7 +121,7 @@ int64_t SystemNative_GetBootTimeTicks(void)
double SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo)
{
-#if defined(HAVE_GETRUSAGE) && !defined(HOST_BROWSER)
+#if defined(HAVE_GETRUSAGE)
uint64_t kernelTime = 0;
uint64_t userTime = 0;
diff --git a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
index 89601399b5b04f..7f147d05f16622 100644
--- a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
+++ b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
@@ -392,24 +392,31 @@ public static bool TryPrintStackTraceFromCrashReport(string crashReportJsonFile,
Console.WriteLine("=========================================");
string? userName = Environment.GetEnvironmentVariable("USER");
- if (!string.IsNullOrEmpty(userName))
+ if (string.IsNullOrEmpty(userName))
{
- if (!RunProcess("sudo", $"chown {userName} {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
+ userName="helixbot";
+ }
- Console.WriteLine("=========================================");
- if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
+ if (!RunProcess("sudo", $"chmod a+rw {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
- Console.WriteLine("=========================================");
- if (!RunProcess("ls", $"-l {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
+ if (!RunProcess("sudo", $"chown {userName} {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ Console.WriteLine("=========================================");
+ if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ Console.WriteLine("=========================================");
+ if (!RunProcess("ls", $"-l {crashReportJsonFile}", Console.Out))
+ {
+ return false;
}
}