-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[browser] single threaded diagnostic server #111910
base: main
Are you sure you want to change the base?
Changes from 5 commits
3cd951f
3bbee71
fe063de
d6c5821
cdad8b2
46aa990
3b711be
685f663
2af88c3
a4d0ac8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe there is same problem that Maraf reported |
||
<_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'" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe there is same problem that Maraf reported |
||
<_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'" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
<!-- Post Wasm MVP features --> | ||
<WasmEnableExceptionHandling Condition="'$(WasmEnableExceptionHandling)' == ''">true</WasmEnableExceptionHandling> | ||
<WasmEnableSIMD Condition="'$(WasmEnableSIMD)' == ''">$(WasmEnableExceptionHandling)</WasmEnableSIMD> | ||
<FeaturePerfTracing Condition="'$(FeaturePerfTracing)' == '' and '$(Configuration)' == 'Debug'">true</FeaturePerfTracing> | ||
</PropertyGroup> | ||
|
||
<Import Project="$(WasmCommonTargetsPath)WasmApp.Common.targets" /> | ||
|
@@ -362,6 +363,8 @@ | |
<EmscriptenEnvVars Include="ENABLE_JS_INTEROP_BY_VALUE=0" Condition="'$(WasmEnableJsInteropByValue)' != 'true'" /> | ||
<EmscriptenEnvVars Include="WASM_ENABLE_SIMD=1" Condition="'$(WasmEnableSIMD)' != 'false'" /> | ||
<EmscriptenEnvVars Include="WASM_ENABLE_SIMD=0" Condition="'$(WasmEnableSIMD)' == 'false'" /> | ||
<EmscriptenEnvVars Include="FEATURE_PERFTRACING=1" Condition="'$(FeaturePerfTracing)' != 'false'" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this in contradiction with default value set on L6? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, this is just passing the value (which doesn't have to be same as default) to emscripten as env variable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What is the reason to set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CoreCLR includes EP always. In Browser, I would like to include it when workload+Debug. Unless user specified There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Mono we have components meaning that an app can either statically or dynamically enable parts of the runtime when linking the app, using the same runtime build. EventPipe uses this (hot reload, debugger as well) and our mobile platforms uses it to enable/disable EventPipe using msbuild flags when building the app. Recommendation is to exclude it for release builds that should be pushed to app stores, but include it for debug and profiling builds of the apps used for debugging or testing. |
||
<EmscriptenEnvVars Include="FEATURE_PERFTRACING=0" Condition="'$(FeaturePerfTracing)' == 'false'" /> | ||
<EmscriptenEnvVars Include="WASM_ENABLE_EH=1" Condition="'$(WasmEnableExceptionHandling)' != 'false'" /> | ||
<EmscriptenEnvVars Include="WASM_ENABLE_EH=0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" /> | ||
<EmscriptenEnvVars Include="ENABLE_AOT_PROFILER=$([System.Convert]::ToInt32($(WasmProfilers.Contains('aot'))))" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the wider Mono specific DISABLE_THREADS here or is this just needed since we have some checks in diagnostics_server.c that currently uses it? If so, then maybe we should change that code to use the eventpipe define for single threaded/disabled threads. Alternative could be to define this based on the more generic event pipe as part of mono specific ds or ep config's. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we are passing it to |
||
PROPERTIES | ||
DISABLE_THREADS ON | ||
) | ||
endif() | ||
|
||
set_target_properties( | ||
eventpipe-mono-objects | ||
PROPERTIES | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will just make this comment once, but it applies through all changes and is basically an effect of the last comment in this review. Multithreaded is the default, so we shouldn't need an explicit define for multithreaded support, instead we should use one define disabling threading. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you prefer |
||
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 ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How frequent will this be called on WASM? When we run regular sample profiler this is called on the timer interval defined in __sampling_rate_in_ns. Looking further down it appears that this method will only be called once, is that correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, I just want to capture the |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we sample on every call? Sample profiler only samples at a specific time interval, like very ms, setup in __sampling_rate_in_ns. Maybe we should only sample based on the sample rate instead on every call to be more inline with how sampling works on all other platforms. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's right, it needs to consider sample frequency. But also I will bring another instrumentation for like safepoint. |
||
{ | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will cause instrumentation on all method calls and it looks like you can't opt out of it meaning you will always take the performance hit even if you won't use sample profiler. Another problem is that you will only be able to sample on function enter, meaning that your samples will be clustered on start of functions. If you have long loops without calls, those will also be blind spots and you won't get any sampling events during that time. Mono has the concept of safepoints when runtime runs in hybrid or coop suspend mode. Using that mechanism would behave similar to how Android/iOS works where we could sample on calls, loop back edges and some other scenarios. Problem is that WASM probably runs in preemptive suspend mode since it only have one thread, meaning that it won't include safepoints in the generated code and we probably don't want to enable cooperate suspend just for this, so in that case we would need to separate the safepoint handling and suspend model, but something would still need to signal the thread to stop on safepoint and right now thread will only suspend its execution and another "thread" will need to do the work (like running GC or sampling) and I assume that won't work on WASM either, meaning piggy back on safepoints for this is probably not an alternative. Is the intention that we should support sampling only when running under interpreter or should this support AOT:ed code as well? When do we leave managed code getting back to the browser event loop? Does it happen when we yield, doing wait calls, other sys calls or do we have other instrumentation that could run browser event loop in the middle of executing managed code? Just trying to understand the execution model to see what alternatives we might have based on WASM execution model. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Safepoint like instrumentation of interp sampling will be next PR. We can't run browser event loop in the middle of executing managed code. |
||
} | ||
|
||
#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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe set this callback to NULL when shutting down, similar to how we do with other profilers in Mono eventpipe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would mean that interp could do IL->IR without instrumentation in the meantime, right ? |
||
#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) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be moved to
https://github.com/dotnet/runtime/blob/bfa02766bdde4657ddd1f734e274d43e6ba3c036/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems#L67
where others are?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what is rooting it for x64 NAOT, when trimmed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This make sure we don't link out the InitializeDefaultEventSources method called during Mono startup to setup runtime specific event sources. Mono root this function through above file and CoreClr does a similar thing, but CoreClr uses additional tasks to root methods called by runtime by scanning different source files, like corlib.h and generate a ILLink.Descriptors.xml as part of build. NativeAOT uses ModuleInitializer preventing this function from being linked out when running ilc on the published app but not exactly sure how its rooting it as part of S.P.C build, but I assume it ends up with similar mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw, why do we need to change fro ILLinkDescriptorsXmls to ILLinkDescriptorsLibraryBuildXml for just his specific item?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We run ILLink again during publish build step on customer dev machine. This file needs to be there to protect it from trimming. With ILLinkDescriptorsXmls it didn't work for me. Is this the right way ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For publish on dev machines, EventSource related logic is kept using linker msbuild property, EventSourceSupport. That is how it works on all other platforms so I assume it should work the same on WASM. It should keep all EventSource related managed code.