diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h index 9c0a120531003a..c712177f868106 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h @@ -512,6 +512,24 @@ 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 (EventPipeEvent *sampling_event) +{ + STATIC_CONTRACT_NOTHROW; + // no-op +} + +static +inline +void +ep_rt_sample_profiler_disabled (void) +{ + STATIC_CONTRACT_NOTHROW; + // no-op +} + static inline void @@ -744,6 +762,15 @@ ep_rt_thread_create ( return ep_rt_aot_thread_create(thread_func, params, thread_type, id); } +static +bool +ep_rt_queue_job ( + void *job_func, + void *params) +{ + EP_UNREACHABLE ("Not implemented in NativeAOT"); +} + 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..f8d17979534c2a 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h @@ -567,6 +567,24 @@ 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 (EventPipeEvent *sampling_event) +{ + STATIC_CONTRACT_NOTHROW; + // no-op +} + +static +inline +void +ep_rt_sample_profiler_disabled (void) +{ + STATIC_CONTRACT_NOTHROW; + // no-op +} + static inline void @@ -871,6 +889,15 @@ ep_rt_thread_create ( return result; } +static +bool +ep_rt_queue_job ( + void *job_func, + void *params) +{ + EP_UNREACHABLE ("Not implemented in CoreCLR"); +} + 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..c4f38e8b6d4d30 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -107,6 +107,9 @@ $(NoWarn),618,67 + + $(NoWarn);CA1416 + $(DefineConstants);MONO_FEATURE_SRE true @@ -114,7 +117,7 @@ true true true - true + true true diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 95f56cea27e8cc..e487f605f29a40 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -27,6 +27,7 @@ 2147483648 true false + true false false emcc @@ -424,6 +425,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)' == 'true'" /> + <_CmakeEnvironmentVariable Include="FEATURE_PERFTRACING=0" Condition="'$(FeaturePerfTracing)' != 'true'" /> <_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 +571,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)' == 'true'" /> + <_MonoRollupEnvironmentVariable Include="FEATURE_PERFTRACING:0" Condition="'$(FeaturePerfTracing)' != 'true'" /> <_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..84206b5c30f2b2 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 @@ + + @@ -434,6 +438,13 @@ <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') + + + <_MonoRuntimeComponentDontLink Include="wasm-bundled-timezones.a" Condition="'$(InvariantTimezone)' == 'true'"/> + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" Condition="'$(FeaturePerfTracing)' != 'true'" /> + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-stub-static.lib" Condition="'$(FeaturePerfTracing)' == 'true'" /> + + diff --git a/src/mono/browser/build/WasmApp.InTree.props b/src/mono/browser/build/WasmApp.InTree.props index b53cc29cbc9880..d81cd25b023b40 100644 --- a/src/mono/browser/build/WasmApp.InTree.props +++ b/src/mono/browser/build/WasmApp.InTree.props @@ -28,7 +28,6 @@ <_MonoRuntimeComponentDontLink Include="libmono-component-debugger-stub-static.a" /> - <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-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..3ace486d3e9cfe 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -698,10 +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_DISABLE_THREADS=1" Condition="'$(WasmEnableThreads)' != 'true'" /> + + + + <_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="-DFEATURE_PERFTRACING_DISABLE_THREADS=1" Condition="'$(WasmEnableThreads)' != 'true'" /> 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 930933b22ab9f3..5273c91f40108a 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. +#ifndef PERFTRACING_DISABLE_THREADS 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; } +#ifndef PERFTRACING_DISABLE_THREADS bool ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( ep_rt_thread_handle_t sampling_thread, @@ -1306,6 +1313,116 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( return true; } +void +ep_rt_mono_sampling_provider_component_init (void) +{ +} + +void +ep_rt_mono_sampling_provider_component_fini (void) +{ +} + +void +ep_rt_mono_sample_profiler_enabled (EventPipeEvent *sampling_event) +{ +} + +void +ep_rt_mono_sample_profiler_disabled (void) +{ +} + +#else // PERFTRACING_DISABLE_THREADS + +bool +ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( + ep_rt_thread_handle_t sampling_thread, + EventPipeEvent *sampling_event) +{ + EP_UNREACHABLE ("Not supported by single threaded runtime."); +} + +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; +} + + +void +ep_rt_mono_sampling_provider_component_init (void) +{ + // 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); +} + +void +ep_rt_mono_sampling_provider_component_fini (void) +{ + EP_ASSERT (_ep_rt_mono_sampling_profiler_provider != NULL); + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_mono_sampling_profiler_provider, NULL); +} + +void +ep_rt_mono_sample_profiler_enabled (EventPipeEvent *sampling_event) +{ + current_sampling_event = sampling_event; + current_sampling_thread = ep_rt_thread_get_handle (); + EP_ASSERT (_ep_rt_mono_sampling_profiler_provider != NULL); + mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, method_enter); +} + +void +ep_rt_mono_sample_profiler_disabled (void) +{ + EP_ASSERT (_ep_rt_mono_sampling_profiler_provider != NULL); + mono_profiler_set_method_enter_callback (_ep_rt_mono_sampling_profiler_provider, NULL); +} + +#endif // PERFTRACING_DISABLE_THREADS + 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..196b183a2d7971 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 (); } @@ -836,6 +837,7 @@ void ep_rt_mono_fini (void) { ep_rt_mono_runtime_provider_fini (); + ep_rt_mono_sampling_provider_component_fini (); ep_rt_mono_profiler_provider_fini (); if (_ep_rt_mono_default_profiler_provider) { diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 94ca5bb94f7e59..d2d1dce5dc66d8 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 (EventPipeEvent *sampling_event); +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 (EventPipeEvent *sampling_event) +{ + ep_rt_mono_sample_profiler_enabled (sampling_event); +} + +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) @@ -668,6 +686,8 @@ ep_rt_byte_array_free (uint8_t *ptr) * Event. */ +#ifndef PERFTRACING_DISABLE_THREADS + static inline void @@ -737,6 +757,69 @@ ep_rt_wait_event_is_valid (ep_rt_wait_event_handle_t *wait_event) return true; } +#else // PERFTRACING_DISABLE_THREADS + +static +inline +void +ep_rt_wait_event_alloc ( + ep_rt_wait_event_handle_t *wait_event, + bool manual, + bool initial) +{ + EP_ASSERT (wait_event != NULL); + wait_event->event = INVALID_HANDLE_VALUE; +} + +static +inline +void +ep_rt_wait_event_free (ep_rt_wait_event_handle_t *wait_event) +{ +} + +static +inline +bool +ep_rt_wait_event_set (ep_rt_wait_event_handle_t *wait_event) +{ + return true; +} + +static +inline +int32_t +ep_rt_wait_event_wait ( + ep_rt_wait_event_handle_t *wait_event, + uint32_t timeout, + bool alertable) +{ + EP_ASSERT (wait_event != NULL && wait_event->event == INVALID_HANDLE_VALUE); + return (int32_t)0; +} + +static +inline +EventPipeWaitHandle +ep_rt_wait_event_get_wait_handle (ep_rt_wait_event_handle_t *wait_event) +{ + EP_ASSERT (wait_event != NULL); + return (EventPipeWaitHandle)wait_event->event; +} + +static +inline +bool +ep_rt_wait_event_is_valid (ep_rt_wait_event_handle_t *wait_event) +{ + if (wait_event == NULL || wait_event->event == NULL || wait_event->event != INVALID_HANDLE_VALUE) + return false; + else + return true; +} + +#endif // PERFTRACING_DISABLE_THREADS + /* * Misc. */ @@ -830,6 +913,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) +#ifndef PERFTRACING_DISABLE_THREADS 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; @@ -867,6 +951,55 @@ ep_rt_thread_create ( return false; } +static +bool +ep_rt_queue_job ( + void *job_func, + void *params) +{ + EP_UNREACHABLE ("Not implemented on in multi threaded"); + return false; +} + +#else // PERFTRACING_DISABLE_THREADS + +static +inline +bool +ep_rt_thread_create ( + void *thread_func, + void *params, + EventPipeThreadType thread_type, + void *id) +{ + EP_UNREACHABLE ("Not implemented on in single threaded"); + return false; +} + +static +bool +ep_rt_queue_job ( + void *job_func, + void *params) +{ + // in single-threaded, it will run the callback inline and re-schedule itself if necessary + // it's called from browser event loop + ds_job_cb cb = (ds_job_cb)job_func; + + // invoke the callback inline for the fist time + gsize done = cb (params); + + // see if it's done or needs to be scheduled again + if (!done) { + // self schedule again + mono_schedule_ds_job (cb, params); + } + + return true; +} + +#endif // PERFTRACING_DISABLE_THREADS + static inline void @@ -880,6 +1013,7 @@ inline void ep_rt_thread_sleep (uint64_t ns) { +#ifndef PERFTRACING_DISABLE_THREADS MONO_REQ_GC_UNSAFE_MODE; if (ns == 0) { mono_thread_info_yield (); @@ -888,6 +1022,7 @@ ep_rt_thread_sleep (uint64_t ns) g_usleep ((gulong)(ns / 1000)); MONO_EXIT_GC_SAFE; } +#endif } static @@ -1970,6 +2105,9 @@ 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_sampling_provider_component_fini (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..82a0d40be64833 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_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,31 @@ 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); + gsize done = reg->cb (reg->data); + if (done){ + THREADS_DEBUG ("mono_wasm_ds_exec done job %p \n", (gpointer)cb); + g_free (reg); + } else { + THREADS_DEBUG ("mono_wasm_ds_exec scheduling job %p again \n", (gpointer)cb); + jobs_ds = g_slist_prepend (jobs_ds, (gpointer)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..0706523a1eab41 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 gsize (*ds_job_cb)(void* data); #ifdef DISABLE_THREADS void mono_main_thread_schedule_background_job (background_job_cb cb); +void mono_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/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets index ca48af019c13c9..8e31675932d494 100644 --- a/src/mono/wasi/build/WasiApp.targets +++ b/src/mono/wasi/build/WasiApp.targets @@ -316,7 +316,8 @@ <_MonoRuntimeComponentDontLink Include="wasm-bundled-timezones.a" Condition="'$(InvariantTimezone)' == 'true'"/> - <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" Condition="'$(UsingWasiRuntimeWorkload)' != 'true'" /> + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" Condition="'$(FeaturePerfTracing)' != 'true'" /> + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-stub-static.lib" Condition="'$(FeaturePerfTracing)' == 'true'" /> diff --git a/src/native/eventpipe/CMakeLists.txt b/src/native/eventpipe/CMakeLists.txt index f7301b5933ae04..8e89519ff6467a 100644 --- a/src/native/eventpipe/CMakeLists.txt +++ b/src/native/eventpipe/CMakeLists.txt @@ -29,7 +29,11 @@ target_include_directories(dn-diagnosticserver-pal INTERFACE ${CMAKE_CURRENT_BIN target_include_directories(dn-diagnosticserver-pal INTERFACE ${SHARED_EVENTPIPE_CONFIG_HEADER_PATH}) 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 ) @@ -41,7 +45,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..21f617325d9a12 --- /dev/null +++ b/src/native/eventpipe/ds-ipc-pal-websocket.c @@ -0,0 +1,554 @@ +// 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->server_url); + 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) +{ + EP_UNREACHABLE ("Not supported by browser WebSocket"); + // NOT SUPPORTED + return false; +} + +DiagnosticsIpcStream * +ds_ipc_accept ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + EP_UNREACHABLE ("Not supported by browser WebSocket"); + // 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_UNREACHABLE ("Not supported by browser WebSocket"); + // NOT SUPPORTED + return 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..f8cd6a5ad52ee3 100644 --- a/src/native/eventpipe/ds-ipc.c +++ b/src/native/eventpipe/ds-ipc.c @@ -454,6 +454,12 @@ ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func call // clear the view. dn_vector_clear (&ipc_poll_handles); + +#ifdef PERFTRACING_DISABLE_THREADS + // in single-threaded mode, we only do one poll + // we can't loop here, that would block the browser event loop + break; +#endif } ep_on_exit: 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..52d5da9cd5da2c 100644 --- a/src/native/eventpipe/ds-server.c +++ b/src/native/eventpipe/ds-server.c @@ -112,6 +112,63 @@ server_warning_callback ( DS_LOG_WARNING_2 ("warning (%d): %s.", code, message); } +static size_t server_loop_tick (void* data) { + 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); + + (void)data; // unused + return 0; // continue +} + +#ifndef PERFTRACING_DISABLE_THREADS EP_RT_DEFINE_THREAD_FUNC (server_thread) { EP_ASSERT (server_volatile_load_shutting_down_state () || ds_ipc_stream_factory_has_active_ports ()); @@ -125,59 +182,10 @@ 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); - } - + while (server_loop_tick (NULL) == 0) { } return (ep_rt_thread_start_func_return_t)0; } +#endif // PERFTRACING_DISABLE_THREADS void ds_server_disable (void) @@ -218,6 +226,7 @@ ds_server_init (void) ds_rt_auto_trace_init (); ds_rt_auto_trace_launch (); +#ifndef PERFTRACING_DISABLE_THREADS ep_rt_thread_id_t thread_id = ep_rt_uint64_t_to_thread_id_t (0); if (!ep_rt_thread_create ((void *)server_thread, NULL, EP_THREAD_TYPE_SERVER, (void *)&thread_id)) { @@ -228,6 +237,9 @@ ds_server_init (void) } else { ds_rt_auto_trace_wait (); } +#else + ep_rt_queue_job ((void *)server_loop_tick, NULL); +#endif } result = true; @@ -258,6 +270,8 @@ ds_server_shutdown (void) void ds_server_pause_for_diagnostics_monitor (void) { +// pause is not implemented for single-threaded +#ifndef PERFTRACING_DISABLE_THREADS _is_paused_for_startup = true; if (ds_ipc_stream_factory_any_suspended_ports ()) { @@ -272,6 +286,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..1b64f670c56332 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 (EventPipeEvent *sampling_event); + +static +void +ep_rt_sample_profiler_disabled (void); + static void ep_rt_notify_profiler_provider_created (EventPipeProvider *provider); @@ -308,6 +316,12 @@ ep_rt_thread_create ( EventPipeThreadType thread_type, void *id); +static +bool +ep_rt_queue_job ( + void *job_func, + void *params); + static void ep_rt_set_server_name (void); diff --git a/src/native/eventpipe/ep-sample-profiler.c b/src/native/eventpipe/ep-sample-profiler.c index b1c10d76a9deb3..01f0c5a287c878 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); } +#ifndef PERFTRACING_DISABLE_THREADS 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,14 +194,18 @@ sample_profiler_enable (void) if (!sample_profiler_load_profiling_enabled ()) { sample_profiler_store_profiling_enabled (true); + ep_rt_sample_profiler_enabled (_thread_time_event); + 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)) EP_UNREACHABLE ("Unable to create sample profiler event."); +#ifndef PERFTRACING_DISABLE_THREADS 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."); +#endif sample_profiler_set_time_granularity (); } @@ -288,6 +294,8 @@ 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. ep_rt_wait_event_wait (&_thread_shutdown_event, EP_INFINITE_WAIT, false); ep_rt_wait_event_free (&_thread_shutdown_event); diff --git a/src/native/eventpipe/ep-session.c b/src/native/eventpipe/ep-session.c index c79d3972cd5587..74395276d9e11d 100644 --- a/src/native/eventpipe/ep-session.c +++ b/src/native/eventpipe/ep-session.c @@ -34,6 +34,8 @@ ep_session_remove_dangling_session_states (EventPipeSession *session); * EventPipeSession. */ +#ifndef PERFTRACING_DISABLE_THREADS + EP_RT_DEFINE_THREAD_FUNC (streaming_thread) { EP_ASSERT (data != NULL); @@ -84,6 +86,27 @@ EP_RT_DEFINE_THREAD_FUNC (streaming_thread) return (ep_rt_thread_start_func_return_t)0; } +#else // PERFTRACING_DISABLE_THREADS + +static size_t streaming_loop_tick(EventPipeSession *const session) { + bool events_written = false; + bool ok; + if (!ep_session_get_streaming_enabled (session)){ + session->streaming_thread = NULL; + return 1; // done + } + EP_GCX_PREEMP_ENTER + ok = ep_session_write_all_buffers_to_file (session, &events_written); + EP_GCX_PREEMP_EXIT + if (!ok) { + ep_disable ((EventPipeSessionID)session); + return 1; // done + } + return 0; // continue +} + +#endif // PERFTRACING_DISABLE_THREADS + static void session_create_streaming_thread (EventPipeSession *session) @@ -98,9 +121,14 @@ session_create_streaming_thread (EventPipeSession *session) if (!ep_rt_wait_event_is_valid (&session->rt_thread_shutdown_event)) EP_UNREACHABLE ("Unable to create stream flushing thread shutdown event."); +#ifndef PERFTRACING_DISABLE_THREADS ep_rt_thread_id_t thread_id = ep_rt_uint64_t_to_thread_id_t (0); if (!ep_rt_thread_create ((void *)streaming_thread, (void *)session, EP_THREAD_TYPE_SESSION, &thread_id)) EP_UNREACHABLE ("Unable to create stream flushing thread."); +#else + ep_rt_volatile_store_uint32_t (&session->started, 1); + ep_rt_queue_job ((void *)streaming_loop_tick, (void *)session); +#endif } static diff --git a/src/native/eventpipe/ep-shared-config.h.in b/src/native/eventpipe/ep-shared-config.h.in index 5b60e03427603e..526b45cbb7d99c 100644 --- a/src/native/eventpipe/ep-shared-config.h.in +++ b/src/native/eventpipe/ep-shared-config.h.in @@ -10,6 +10,16 @@ #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_DISABLE_THREADS +#ifdef FEATURE_PERFTRACING_DISABLE_THREADS +#define PERFTRACING_DISABLE_THREADS +#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; } }