diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.c b/src/mono/mono/eventpipe/ep-buffer-manager.c index 8e05ae37ed4263..07c5c5f03d0aa8 100644 --- a/src/mono/mono/eventpipe/ep-buffer-manager.c +++ b/src/mono/mono/eventpipe/ep-buffer-manager.c @@ -423,13 +423,6 @@ buffer_manager_allocate_buffer_for_thread ( // Allocating a buffer requires us to take the lock. EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section1) - - // if we are deallocating then give up, see the comments in ep_buffer_manager_suspend_write_event () for why this is important. - if ((bool)ep_rt_volatile_load_uint32_t (&buffer_manager->write_event_suspending)) { - *write_suspended = true; - ep_raise_error_holding_spin_lock (section1); - } - thread_buffer_list = ep_thread_session_state_get_buffer_list (thread_session_state); if (thread_buffer_list == NULL) { thread_buffer_list = ep_buffer_list_alloc (buffer_manager, ep_thread_session_state_get_thread (thread_session_state)); @@ -797,8 +790,6 @@ ep_buffer_manager_alloc ( instance->session = session; instance->size_of_all_buffers = 0; - ep_rt_volatile_store_uint32_t (&instance->write_event_suspending, (uint32_t)false); - #ifdef EP_CHECKED_BUILD instance->num_buffers_allocated = 0; instance->num_buffers_stolen = 0; @@ -837,8 +828,6 @@ ep_buffer_manager_free (EventPipeBufferManager * buffer_manager) { ep_return_void_if_nok (buffer_manager != NULL); - ep_rt_volatile_store_uint32_t (&buffer_manager->write_event_suspending, (uint32_t)true); - ep_buffer_manager_deallocate_buffers (buffer_manager); ep_rt_wait_event_free (&buffer_manager->rt_wait_event); @@ -931,10 +920,6 @@ ep_buffer_manager_write_event ( thread_lock = ep_thread_get_rt_lock_ref (current_thread); EP_SPIN_LOCK_ENTER (thread_lock, section1) - if (ep_rt_volatile_load_uint32_t_without_barrier (&buffer_manager->write_event_suspending) != (uint32_t)false) - // This session is suspending, we need to avoid initializing any session state and exit - ep_raise_error_holding_spin_lock (section1); - session_state = ep_thread_get_or_create_session_state (current_thread, session); ep_raise_error_if_nok_holding_spin_lock (session_state != NULL, section1); @@ -977,16 +962,6 @@ ep_buffer_manager_write_event ( thread_lock = ep_thread_get_rt_lock_ref (current_thread); EP_SPIN_LOCK_ENTER (thread_lock, section3) - if (ep_rt_volatile_load_uint32_t_without_barrier (&buffer_manager->write_event_suspending) != (uint32_t)false) { - // After leaving the manager's lock in buffer_manager_allocated_buffer_for_thread some other thread decided to suspend writes. - // We need to immediately return the buffer we just took without storing it or writing to it. - // suspend_write_event () is spinning waiting for this buffer to be relinquished. - ep_buffer_convert_to_read_only (buffer); - - // We treat this as the write_event() call occurring after this session stopped listening for events, effectively the - // same as if ep_event_is_enabled returned false. - ep_raise_error_holding_spin_lock (section3); - } else { ep_thread_session_state_set_write_buffer (session_state, buffer); // Try to write the event after we allocated a buffer. // This is the first time if the thread had no buffers before the call to this function. @@ -994,7 +969,6 @@ ep_buffer_manager_write_event ( alloc_new_buffer = !ep_buffer_write_event (buffer, event_thread, session, ep_event, payload, activity_id, related_activity_id, stack); EP_ASSERT(!alloc_new_buffer); ep_thread_session_state_increment_sequence_number (session_state); - } EP_SPIN_LOCK_EXIT (thread_lock, section3) } } @@ -1034,28 +1008,22 @@ ep_buffer_manager_suspend_write_event ( ep_rt_thread_array_alloc (&thread_array); EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section1); EP_ASSERT (ep_buffer_manager_ensure_consistency (buffer_manager) == true); - ep_rt_volatile_store_uint32_t (&buffer_manager->write_event_suspending, (uint32_t)true); - - // From this point until write_event_suspending is reset to false it is impossible - // for new EventPipeThreadSessionStates to be added to the thread_session_state_list or - // for new EventBuffers to be added to an existing EventPipeBufferList. The only - // way ep_buffer_manager_allocate_buffer_for_thread is allowed to add one is by: - // 1) take rt_lock - ep_buffer_manager_allocate_buffer_for_thread can't own it now because this thread owns it, - // but after this thread gives it up lower in this function it could be acquired. - // 2) observe write_event_suspending = false - that won't happen, acquiring rt_lock - // guarantees ep_buffer_manager_allocate_buffer_for_thread will observe all the memory changes this - // thread made prior to releasing m_lock and we've already set it true. - // This ensures that we iterate over the list of threads below we've got the complete list. + // Find all threads that have used this buffer manager. ep_rt_thread_session_state_list_iterator_t thread_session_state_list_iterator; ep_rt_thread_session_state_list_iterator_begin (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator); while (!ep_rt_thread_session_state_list_iterator_end (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator)) { - ep_rt_thread_array_append (&thread_array, ep_thread_session_state_get_thread (ep_rt_thread_session_state_list_iterator_value (&thread_session_state_list_iterator))); + EventPipeThread *thread = ep_thread_session_state_get_thread (ep_rt_thread_session_state_list_iterator_value (&thread_session_state_list_iterator)); + ep_rt_thread_array_append (&thread_array, thread); ep_rt_thread_session_state_list_iterator_next (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator); + + // Once EventPipeSession::SuspendWriteEvent completes, we shouldn't have any + // in progress writes left. + EP_ASSERT (ep_thread_get_session_write_in_progress (thread) != session_index); } EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section1); - // Iterate through all the threads, forcing them to finish writes in progress inside EventPipeThread::m_lock, - // relinquish any buffers stored in EventPipeThread::m_pWriteBuffer and prevent storing new ones. + // Iterate through all the threads, forcing them to relinquish any buffers stored in + // EventPipeThread's write buffer and prevent storing new ones. ep_rt_thread_array_iterator_t thread_array_iterator; ep_rt_thread_array_iterator_begin (&thread_array, &thread_array_iterator); while (!ep_rt_thread_array_iterator_end (&thread_array, &thread_array_iterator)) { @@ -1063,44 +1031,10 @@ ep_buffer_manager_suspend_write_event ( EP_SPIN_LOCK_ENTER (ep_thread_get_rt_lock_ref (thread), section2) EventPipeThreadSessionState *thread_session_state = ep_thread_get_session_state (thread, buffer_manager->session); ep_thread_session_state_set_write_buffer (thread_session_state, NULL); - // From this point until write_event_suspending is reset to false it is impossible - // for this thread to set the write buffer to a non-null value which in turn means - // it can't write events into any buffer. To do this it would need to both: - // 1) Acquire the thread lock - it can't right now but it will be able to do so after - // we release the lock below - // 2) Observe write_event_suspending = false - that won't happen, acquiring the thread - // lock guarantees ep_buffer_manager_write_event will observe all the memory - // changes this thread made prior to releasing the thread - // lock and we already set it true. EP_SPIN_LOCK_EXIT (ep_thread_get_rt_lock_ref (thread), section2) ep_rt_thread_array_iterator_next (&thread_array, &thread_array_iterator); } - // Wait for any straggler ep_buffer_manager_write_event threads that may have already allocated a buffer but - // hadn't yet relinquished it. - ep_rt_thread_session_state_list_iterator_t thread_session_state_list_iterator; - EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section3) - ep_rt_thread_session_state_list_iterator_begin (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator); - while (!ep_rt_thread_session_state_list_iterator_end (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator)) { - EventPipeBufferList *buffer_list = ep_thread_session_state_get_buffer_list (ep_rt_thread_session_state_list_iterator_value (&thread_session_state_list_iterator)); - if (buffer_list) { - EventPipeThread *const event_pipe_thread = ep_buffer_list_get_thread (buffer_list); - if (event_pipe_thread) { - EP_YIELD_WHILE (ep_thread_get_session_write_in_progress (event_pipe_thread) == session_index); - // It still guarantees that the thread has returned its buffer, but it also now guarantees that - // that the thread has returned from ep_session_write_event () and has relinquished the session pointer - // This yield is guaranteed to eventually finish because threads will eventually exit write_event () - // setting the flag back to -1. If the thread could quickly re-enter WriteEvent and set the flag - // back to this_session_id we could theoretically get unlucky and never observe the gap, but - // setting s_pSessions[this_session_id] = NULL above guaranteed that can't happen indefinately. - // Sooner or later the thread is going to see the NULL value and once it does it won't store - // this_session_id into the flag again. - } - } - ep_rt_thread_session_state_list_iterator_next (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator); - } - EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section3) - ep_on_exit: ep_requires_lock_held (); ep_rt_thread_array_free (&thread_array); @@ -1338,15 +1272,6 @@ ep_buffer_manager_deallocate_buffers (EventPipeBufferManager *buffer_manager) // Take the buffer manager manipulation lock EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section1) EP_ASSERT (ep_buffer_manager_ensure_consistency (buffer_manager)); - EP_ASSERT ((bool)ep_rt_volatile_load_uint32_t (&buffer_manager->write_event_suspending)); - - // This m_writeEventSuspending flag + locks ensures that no thread will touch any of the - // state we are dismantling here. This includes: - // a) EventPipeThread m_sessions[session_id] - // b) EventPipeThreadSessionState - // c) EventPipeBufferList - // d) EventPipeBuffer - // e) EventPipeBufferManager.m_pThreadSessionStateList ep_rt_thread_session_state_list_iterator_t thread_session_state_list_iterator; ep_rt_thread_session_state_list_iterator_begin (&buffer_manager->thread_session_state_list, &thread_session_state_list_iterator); diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.h b/src/mono/mono/eventpipe/ep-buffer-manager.h index ab1e73ea5c8a89..18ea00e377af2a 100644 --- a/src/mono/mono/eventpipe/ep-buffer-manager.h +++ b/src/mono/mono/eventpipe/ep-buffer-manager.h @@ -134,7 +134,6 @@ struct _EventPipeBufferManager_Internal { uint32_t num_buffers_stolen; uint32_t num_buffers_leaked; #endif - volatile uint32_t write_event_suspending; }; #if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_BUFFER_MANAGER_GETTER_SETTER) diff --git a/src/mono/mono/eventpipe/ep-event-source.c b/src/mono/mono/eventpipe/ep-event-source.c index 7db34bee58c755..10b8b47aaf749f 100644 --- a/src/mono/mono/eventpipe/ep-event-source.c +++ b/src/mono/mono/eventpipe/ep-event-source.c @@ -190,6 +190,8 @@ ep_event_source_enable ( EP_ASSERT (event_source != NULL); EP_ASSERT (session != NULL); + ep_requires_lock_held (); + EventPipeSessionProvider *session_provider = ep_session_provider_alloc (event_source->provider_name, (uint64_t)-1, EP_EVENT_LEVEL_LOG_ALWAYS, NULL); if (session_provider != NULL) ep_session_add_session_provider (session, session_provider); @@ -207,8 +209,8 @@ ep_event_source_send_process_info ( ep_char16_t *arch_info_utf16 = NULL; command_line_utf16 = ep_rt_utf8_to_utf16_string (command_line, -1); - os_info_utf16 = ep_rt_utf8_to_utf16_string (_ep_os_info, -1); - arch_info_utf16 = ep_rt_utf8_to_utf16_string (_ep_arch_info, -1); + os_info_utf16 = ep_rt_utf8_to_utf16_string (ep_event_source_get_os_info (), -1); + arch_info_utf16 = ep_rt_utf8_to_utf16_string (ep_event_source_get_arch_info (), -1); EventData data [3] = { { 0 } }; if (command_line_utf16) diff --git a/src/mono/mono/eventpipe/ep-event-source.h b/src/mono/mono/eventpipe/ep-event-source.h index 7fb1d02617d72e..350853ce81c33c 100644 --- a/src/mono/mono/eventpipe/ep-event-source.h +++ b/src/mono/mono/eventpipe/ep-event-source.h @@ -34,6 +34,24 @@ struct _EventPipeEventSource { }; #endif +static +inline +const char * +ep_event_source_get_os_info (void) +{ + extern const char *_ep_os_info; + return _ep_os_info; +} + +static +inline +const char * +ep_event_source_get_arch_info (void) +{ + extern const char *_ep_arch_info; + return _ep_arch_info; +} + EventPipeEventSource * ep_event_source_alloc (void); diff --git a/src/mono/mono/eventpipe/ep-event.h b/src/mono/mono/eventpipe/ep-event.h index e2fe6729e1ebde..40874166b1248c 100644 --- a/src/mono/mono/eventpipe/ep-event.h +++ b/src/mono/mono/eventpipe/ep-event.h @@ -52,8 +52,11 @@ struct _EventPipeEvent { EP_DEFINE_GETTER(EventPipeEvent *, event, uint64_t, keywords) EP_DEFINE_GETTER_REF(EventPipeEvent *, event, volatile int64_t *, enabled_mask) EP_DEFINE_SETTER(EventPipeEvent *, event, int64_t, enabled_mask) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint8_t *, metadata) EP_DEFINE_GETTER(EventPipeEvent *, event, EventPipeProvider *, provider) EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, event_id) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, event_version) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, metadata_len) EP_DEFINE_GETTER(EventPipeEvent *, event, EventPipeEventLevel, level) EP_DEFINE_GETTER(EventPipeEvent *, event, bool, need_stack) diff --git a/src/mono/mono/eventpipe/ep-file.c b/src/mono/mono/eventpipe/ep-file.c index 42930b4ece57c9..6ee792baa69bce 100644 --- a/src/mono/mono/eventpipe/ep-file.c +++ b/src/mono/mono/eventpipe/ep-file.c @@ -340,6 +340,8 @@ ep_file_alloc ( // Start at 0 - The value is always incremented prior to use, so the first ID will be 1. instance->stack_id_counter = 0; + ep_rt_volatile_store_uint32_t (&instance->initialized, 0); + #ifdef EP_CHECKED_BUILD instance->last_sorted_timestamp = ep_perf_counter_query (); #endif @@ -368,9 +370,9 @@ ep_file_free (EventPipeFile *file) ep_rt_metadata_labels_free (&file->metadata_ids); ep_rt_stack_hash_free (&file->stack_hash); - // If there's no fast_serializer, stream_writer ownership + // If file has not been initialized, stream_writer ownership // have not been passed along and needs to be freed by file. - if (!file->fast_serializer) + if (ep_rt_volatile_load_uint32_t (&file->initialized) == 0) ep_stream_writer_free_vcall (file->stream_writer); ep_fast_serializable_object_fini (&file->fast_serializable_object); @@ -393,6 +395,7 @@ ep_file_initialize_file (EventPipeFile *file) } if (success) { + ep_rt_volatile_store_uint32_t (&file->initialized, 1); // Create the file stream and write the FastSerialization header. file->fast_serializer = ep_fast_serializer_alloc (file->stream_writer); diff --git a/src/mono/mono/eventpipe/ep-file.h b/src/mono/mono/eventpipe/ep-file.h index f3dd4851b5742e..62940be46934db 100644 --- a/src/mono/mono/eventpipe/ep-file.h +++ b/src/mono/mono/eventpipe/ep-file.h @@ -48,6 +48,7 @@ struct _EventPipeFile_Internal { uint32_t sampling_rate_in_ns; uint32_t stack_id_counter; volatile uint32_t metadata_id_counter; + volatile uint32_t initialized; // The format to serialize. EventPipeSerializationFormat format; }; diff --git a/src/mono/mono/eventpipe/ep-provider.c b/src/mono/mono/eventpipe/ep-provider.c index 819c414da416c0..d05fc3ae25b19f 100644 --- a/src/mono/mono/eventpipe/ep-provider.c +++ b/src/mono/mono/eventpipe/ep-provider.c @@ -257,6 +257,24 @@ ep_provider_add_event ( ep_exit_error_handler (); } +void +ep_provider_set_delete_deferred ( + EventPipeProvider *provider, + bool deferred) +{ + EP_ASSERT (provider != NULL); + provider->delete_deferred = deferred; + + // EventSources will be collected once they ungregister themselves, + // so we can't call back in to them. + if (provider->callback_func && provider->callback_data_free_func) + provider->callback_data_free_func (provider->callback_func, provider->callback_data); + + provider->callback_func = NULL; + provider->callback_data_free_func = NULL; + provider->callback_data = NULL; +} + const EventPipeProviderCallbackData * provider_set_config ( EventPipeProvider *provider, diff --git a/src/mono/mono/eventpipe/ep-provider.h b/src/mono/mono/eventpipe/ep-provider.h index 059e84e6cc7573..645c4f78eb844b 100644 --- a/src/mono/mono/eventpipe/ep-provider.h +++ b/src/mono/mono/eventpipe/ep-provider.h @@ -56,7 +56,6 @@ struct _EventPipeProvider { EP_DEFINE_GETTER(EventPipeProvider *, provider, const ep_char8_t *, provider_name) EP_DEFINE_GETTER(EventPipeProvider *, provider, const ep_char16_t *, provider_name_utf16) EP_DEFINE_GETTER(EventPipeProvider *, provider, bool, delete_deferred) -EP_DEFINE_SETTER(EventPipeProvider *, provider, bool, delete_deferred) EP_DEFINE_GETTER(EventPipeProvider *, provider, uint64_t, sessions) static @@ -116,5 +115,10 @@ ep_provider_add_event ( const uint8_t *metadata, uint32_t metadata_len); +void +ep_provider_set_delete_deferred ( + EventPipeProvider *provider, + bool deferred); + #endif /* ENABLE_PERFTRACING */ #endif /** __EVENTPIPE_PROVIDER_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 5aa604872db0ea..04808f23b9768f 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -75,7 +75,17 @@ #define EP_RT_DEFINE_ARRAY(array_name, array_type, item_type) \ static inline void ep_rt_ ## array_name ## _alloc (array_type *ep_array) { ep_array->array = g_array_new (FALSE, FALSE, sizeof (item_type)); } \ static inline void ep_rt_ ## array_name ## _free (array_type *ep_array) { g_array_free (ep_array->array, TRUE); } \ + static inline void ep_rt_ ## array_name ## _clear (array_type *ep_array) { g_array_set_size (ep_array->array, 0); } \ static inline void ep_rt_ ## array_name ## _append (array_type *ep_array, item_type item) { g_array_append_val (ep_array->array, item); } \ + static inline bool ep_rt_ ## array_name ## _remove (array_type *ep_array, const item_type item) { \ + for (gint i = 0; i < ep_array->array->len; ++i ) { \ + if (g_array_index (ep_array->array, item_type, i) == item) { \ + ep_array->array = g_array_remove_index_fast (ep_array->array, i); \ + return true; \ + } \ + } \ + return false; \ + } \ static inline size_t ep_rt_ ## array_name ## _size (const array_type *ep_array) { return ep_array->array->len; } #define EP_RT_DEFINE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ @@ -374,6 +384,9 @@ ep_rt_atomic_dec_int64_t (volatile int64_t *value) * EventPipe. */ +EP_RT_DEFINE_ARRAY (session_id_array, ep_rt_session_id_array_t, EventPipeSessionID) +EP_RT_DEFINE_ARRAY_ITERATOR (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) + static inline EventPipeThreadHolder * @@ -617,7 +630,21 @@ ep_rt_sample_profiler_get_sampling_rate (void) static inline void -ep_rt_sample_set_sampling_rate (uint32_t nanoseconds) +ep_rt_sample_profiler_set_sampling_rate (uint32_t nanoseconds) +{ + //TODO: Not supported. +} + +static +void +ep_rt_sample_profiler_can_start_sampling (void) +{ + //TODO: Not supported. +} + +static +void +ep_rt_notify_profiler_provider_created (EventPipeProvider *provider) { //TODO: Not supported. } diff --git a/src/mono/mono/eventpipe/ep-rt-types-mono.h b/src/mono/mono/eventpipe/ep-rt-types-mono.h index 90f2f6d4b220f9..fbea8005d6666e 100644 --- a/src/mono/mono/eventpipe/ep-rt-types-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-types-mono.h @@ -109,6 +109,9 @@ typedef struct _rt_mono_array_iterator_internal_t ep_rt_buffer_list_array_iterat typedef struct _rt_mono_array_internal_t ep_rt_thread_array_t; typedef struct _rt_mono_array_iterator_internal_t ep_rt_thread_array_iterator_t; +typedef struct _rt_mono_array_internal_t ep_rt_session_id_array_t; +typedef struct _rt_mono_array_iterator_internal_t ep_rt_session_id_array_iterator_t; + typedef MonoThreadHandle ep_rt_thread_handle_t; typedef gpointer ep_rt_file_handle_t; diff --git a/src/mono/mono/eventpipe/ep-rt.h b/src/mono/mono/eventpipe/ep-rt.h index 46b2cfc9ab3731..89b73ff47d3dae 100644 --- a/src/mono/mono/eventpipe/ep-rt.h +++ b/src/mono/mono/eventpipe/ep-rt.h @@ -43,7 +43,9 @@ #define EP_RT_DECLARE_ARRAY(array_name, array_type, item_type) \ static void ep_rt_ ## array_name ## _alloc (array_type *ep_array); \ static void ep_rt_ ## array_name ## _free (array_type *ep_array); \ + static void ep_rt_ ## array_name ## _clear (array_type *ep_array); \ static void ep_rt_ ## array_name ## _append (array_type *ep_array, item_type item); \ + static bool ep_rt_ ## array_name ## _remove (array_type *ep_array, const item_type item); \ static size_t ep_rt_ ## array_name ## _size (const array_type *ep_array); #define EP_RT_DECLARE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ @@ -100,6 +102,9 @@ ep_rt_atomic_dec_int64_t (volatile int64_t *value); * EventPipe. */ +EP_RT_DECLARE_ARRAY (session_id_array, ep_rt_session_id_array_t, EventPipeSessionID) +EP_RT_DECLARE_ARRAY_ITERATOR (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) + static void ep_rt_init (void); @@ -227,7 +232,15 @@ ep_rt_sample_profiler_get_sampling_rate (void); static void -ep_rt_sample_set_sampling_rate (uint32_t nanoseconds); +ep_rt_sample_profiler_set_sampling_rate (uint32_t nanoseconds); + +static +void +ep_rt_sample_profiler_can_start_sampling (void); + +static +void +ep_rt_notify_profiler_provider_created (EventPipeProvider *provider); /* * EventPipeSessionProvider. diff --git a/src/mono/mono/eventpipe/ep-session.c b/src/mono/mono/eventpipe/ep-session.c index b5e84584be4937..cd7b4aeb4c2064 100644 --- a/src/mono/mono/eventpipe/ep-session.c +++ b/src/mono/mono/eventpipe/ep-session.c @@ -11,6 +11,7 @@ #include "ep-event.h" #include "ep-file.h" #include "ep-session.h" +#include "ep-event-payload.h" #include "ep-rt.h" /* @@ -42,9 +43,10 @@ void session_disable_ipc_streaming_thread (EventPipeSession *session) { EP_ASSERT (session->session_type == EP_SESSION_TYPE_IPCSTREAM); - EP_ASSERT (ep_session_get_ipc_streaming_enabled (session)); + EP_ASSERT (ep_session_get_ipc_streaming_enabled (session) == true); EP_ASSERT (!ep_rt_process_detach ()); + EP_ASSERT (session->buffer_manager != NULL); // The IPC streaming thread will watch this value and exit // when profiling is disabled. @@ -70,13 +72,14 @@ ep_session_alloc ( uint32_t circular_buffer_size_in_mb, const EventPipeProviderConfiguration *providers, uint32_t providers_len, - bool rundown_enabled) + EventPipeSessionSynchronousCallback sync_callback) { EP_ASSERT (index < EP_MAX_NUMBER_OF_SESSIONS); EP_ASSERT (format < EP_SERIALIZATION_FORMAT_COUNT); - EP_ASSERT (circular_buffer_size_in_mb > 0); + EP_ASSERT (session_type == EP_SESSION_TYPE_SYNCHRONOUS || circular_buffer_size_in_mb > 0); EP_ASSERT (providers_len > 0); EP_ASSERT (providers != NULL); + EP_ASSERT ((sync_callback != NULL) == (session_type == EP_SESSION_TYPE_SYNCHRONOUS)); ep_requires_lock_held (); @@ -91,10 +94,11 @@ ep_session_alloc ( ep_raise_error_if_nok (instance->providers != NULL); instance->index = index; - instance->rundown_enabled = rundown_enabled ? 1 : 0; + instance->rundown_enabled = 0; instance->session_type = session_type; instance->format = format; instance->rundown_requested = rundown_requested; + instance->synchronous_callback = sync_callback; // Hard coded 10MB for now, we'll probably want to make // this configurable later. @@ -102,8 +106,10 @@ ep_session_alloc ( sequence_point_alloc_budget = 10 * 1024 * 1024; } - instance->buffer_manager = ep_buffer_manager_alloc (instance, ((size_t)circular_buffer_size_in_mb) << 20, sequence_point_alloc_budget); - ep_raise_error_if_nok (instance->buffer_manager != NULL); + if (session_type != EP_SESSION_TYPE_SYNCHRONOUS) { + instance->buffer_manager = ep_buffer_manager_alloc (instance, ((size_t)circular_buffer_size_in_mb) << 20, sequence_point_alloc_budget); + ep_raise_error_if_nok (instance->buffer_manager != NULL); + } // Create the event pipe file. // A NULL output path means that we should not write the results to a file. @@ -243,9 +249,34 @@ ep_session_suspend_write_event (EventPipeSession *session) { EP_ASSERT (session != NULL); - // Force all in-progress writes to either finish or cancel - // This is required to ensure we can safely flush and delete the buffers - ep_buffer_manager_suspend_write_event (session->buffer_manager, session->index); + // Need to disable the session before calling this method. + EP_ASSERT (ep_is_session_enabled ((EventPipeSessionID)session) == false); + + ep_rt_thread_array_t threads; + ep_rt_thread_array_alloc (&threads); + + ep_thread_get_threads (&threads); + + ep_rt_thread_array_iterator_t threads_iterator; + ep_rt_thread_array_iterator_begin (&threads, &threads_iterator); + while (!ep_rt_thread_array_iterator_end (&threads, &threads_iterator)) { + EventPipeThread *thread = ep_rt_thread_array_iterator_value (&threads_iterator); + if (thread) { + // Wait for the thread to finish any writes to this session + EP_YIELD_WHILE (ep_thread_get_session_write_in_progress (thread) == session->index); + + // Since we've already disabled the session, the thread won't call back in to this + // session once its done with the current write + ep_thread_release (thread); + } + ep_rt_thread_array_iterator_next (&threads, &threads_iterator); + } + + ep_rt_thread_array_free (&threads); + + if (session->buffer_manager) + // Convert all buffers to read only to ensure they get flushed + ep_buffer_manager_suspend_write_event (session->buffer_manager, session->index); } void @@ -253,7 +284,7 @@ ep_session_write_sequence_point_unbuffered (EventPipeSession *session) { EP_ASSERT (session != NULL); - ep_return_void_if_nok (session->file != NULL); + ep_return_void_if_nok (session->file != NULL && session->buffer_manager != NULL); EventPipeSequencePoint sequence_point; ep_sequence_point_init (&sequence_point); @@ -275,6 +306,11 @@ ep_session_start_streaming (EventPipeSession *session) if (session->session_type == EP_SESSION_TYPE_IPCSTREAM) session_create_ipc_streaming_thread (session); + if (session->session_type == EP_SESSION_TYPE_SYNCHRONOUS) { + EP_ASSERT (session->file == NULL); + EP_ASSERT (!ep_session_get_ipc_streaming_enabled (session)); + } + ep_requires_lock_held (); return; } @@ -290,6 +326,9 @@ void ep_session_add_session_provider (EventPipeSession *session, EventPipeSessionProvider *session_provider) { EP_ASSERT (session != NULL); + + ep_requires_lock_held (); + ep_session_provider_list_add_session_provider (session->providers, session_provider); } @@ -311,7 +350,7 @@ ep_session_write_all_buffers_to_file (EventPipeSession *session, bool *events_wr { EP_ASSERT (session != NULL); - if (session->file == NULL) + if (session->file == NULL || session->buffer_manager == NULL) return true; // Get the current time stamp. @@ -323,7 +362,7 @@ ep_session_write_all_buffers_to_file (EventPipeSession *session, bool *events_wr } bool -ep_session_write_event_buffered ( +ep_session_write_event ( EventPipeSession *session, EventPipeThread *thread, EventPipeEvent *ep_event, @@ -336,18 +375,41 @@ ep_session_write_event_buffered ( EP_ASSERT (session != NULL); EP_ASSERT (ep_event != NULL); - return ep_event_is_enabled_by_mask (ep_event, ep_session_get_mask (session)) ? - ep_buffer_manager_write_event ( - session->buffer_manager, - thread, - session, - ep_event, - payload, - activity_id, - related_activity_id, - event_thread, - stack) : - false; + bool result = false; + + // Filter events specific to "this" session based on precomputed flag on provider/events. + if (ep_event_is_enabled_by_mask (ep_event, ep_session_get_mask (session))) { + if (session->synchronous_callback) { + session->synchronous_callback ( + ep_event_get_provider (ep_event), + ep_event_get_event_id (ep_event), + ep_event_get_event_version (ep_event), + ep_event_get_metadata_len (ep_event), + ep_event_get_metadata (ep_event), + ep_event_payload_get_size (payload), + ep_event_payload_get_flat_data (payload), + activity_id, + related_activity_id, + event_thread, + stack == NULL ? 0 : ep_stack_contents_get_size (stack), + stack == NULL ? NULL : (uintptr_t *)ep_stack_contents_get_pointer (stack)); + result = true; + } else { + EP_ASSERT (session->buffer_manager != NULL); + result = ep_buffer_manager_write_event ( + session->buffer_manager, + thread, + session, + ep_event, + payload, + activity_id, + related_activity_id, + event_thread, + stack); + } + } + + return result; } EventPipeEventInstance * @@ -355,6 +417,12 @@ ep_session_get_next_event (EventPipeSession *session) { EP_ASSERT (session != NULL); ep_requires_lock_not_held (); + + if (!session->buffer_manager) { + EP_ASSERT (!"Shouldn't call get_next_event on a synchronous session."); + return NULL; + } + return ep_buffer_manager_get_next_event (session->buffer_manager); } @@ -362,7 +430,12 @@ EventPipeWaitHandle ep_session_get_wait_event (EventPipeSession *session) { EP_ASSERT (session != NULL); - EP_ASSERT (session->buffer_manager != NULL); + + if (!session->buffer_manager) { + EP_ASSERT (!"Shouldn't call get_wait_event on a synchronous session."); + return (EventPipeWaitHandle)NULL; + } + return ep_rt_wait_event_get_wait_handle (ep_buffer_manager_get_rt_wait_event_ref (session->buffer_manager)); } diff --git a/src/mono/mono/eventpipe/ep-session.h b/src/mono/mono/eventpipe/ep-session.h index 8f0352d87c3209..6755a19a436e1c 100644 --- a/src/mono/mono/eventpipe/ep-session.h +++ b/src/mono/mono/eventpipe/ep-session.h @@ -34,6 +34,8 @@ struct _EventPipeSession_Internal { EventPipeBufferManager *buffer_manager; // Object used to flush event data (File, IPC stream, etc.). EventPipeFile *file; + // For synchoronous sessions. + EventPipeSessionSynchronousCallback synchronous_callback; // Start date and time in UTC. ep_systemtime_t session_start_time; // Start timestamp. @@ -79,7 +81,7 @@ ep_session_alloc ( uint32_t circular_buffer_size_in_mb, const EventPipeProviderConfiguration *providers, uint32_t providers_len, - bool rundown_enabled); + EventPipeSessionSynchronousCallback sync_callback); void ep_session_free (EventPipeSession *session); @@ -137,8 +139,12 @@ ep_session_write_all_buffers_to_file ( EventPipeSession *session, bool *events_written); +// If a session is non-synchronous (i.e. a file, pipe, etc) WriteEvent will +// put the event in a buffer and return as quick as possible. If a session is +// synchronous (callback to the profiler) then this method will block until the +// profiler is done parsing and reacting to it. bool -ep_session_write_event_buffered ( +ep_session_write_event ( EventPipeSession *session, EventPipeThread *thread, EventPipeEvent *ep_event, diff --git a/src/mono/mono/eventpipe/ep-stream.c b/src/mono/mono/eventpipe/ep-stream.c index b936269d0b8006..ae0524482898b6 100644 --- a/src/mono/mono/eventpipe/ep-stream.c +++ b/src/mono/mono/eventpipe/ep-stream.c @@ -374,10 +374,7 @@ ep_file_stream_writer_alloc (const ep_char8_t *output_file_path) instance->file_stream = ep_file_stream_alloc (); ep_raise_error_if_nok (instance->file_stream != NULL); - if (!ep_file_stream_open_write (instance->file_stream, output_file_path)) { - EP_ASSERT (!"Unable to open file for write."); - ep_raise_error (); - } + ep_raise_error_if_nok (ep_file_stream_open_write (instance->file_stream, output_file_path) == true); ep_on_exit: return instance; diff --git a/src/mono/mono/eventpipe/ep-thread.c b/src/mono/mono/eventpipe/ep-thread.c index b2e8043888a676..8f2573badcbb3d 100644 --- a/src/mono/mono/eventpipe/ep-thread.c +++ b/src/mono/mono/eventpipe/ep-thread.c @@ -11,6 +11,9 @@ #include "ep-session.h" #include "ep-rt.h" +static ep_rt_spin_lock_handle_t _ep_threads_lock = {0}; +static ep_rt_thread_array_t _ep_threads = {0}; + /* * Forward declares of all static functions. */ @@ -35,6 +38,10 @@ ep_thread_alloc (void) instance->os_thread_id = ep_rt_current_thread_get_id (); memset (instance->session_state, 0, sizeof (instance->session_state)); + ep_rt_spin_lock_aquire (&_ep_threads_lock); + ep_rt_thread_array_append (&_ep_threads, instance); + ep_rt_spin_lock_release (&_ep_threads_lock); + ep_on_exit: return instance; @@ -57,6 +64,12 @@ ep_thread_free (EventPipeThread *thread) } #endif + ep_rt_spin_lock_aquire (&_ep_threads_lock); + // Remove ourselves from the global list + if (EP_UNLIKELY (!ep_rt_thread_array_remove (&_ep_threads, thread))) + EP_ASSERT (!"We couldn't find ourselves in the global thread list"); + ep_rt_spin_lock_release (&_ep_threads_lock); + ep_rt_spin_lock_free (&thread->rt_lock); ep_rt_object_free (thread); } @@ -76,6 +89,21 @@ ep_thread_release (EventPipeThread *thread) ep_thread_free (thread); } +void +ep_thread_init (void) +{ + ep_rt_spin_lock_alloc (&_ep_threads_lock); + ep_rt_thread_array_alloc (&_ep_threads); +} + +void +ep_thread_fini (void) +{ + EP_ASSERT (ep_rt_thread_array_size (&_ep_threads) == 0); + ep_rt_thread_array_free (&_ep_threads); + ep_rt_spin_lock_free (&_ep_threads_lock); +} + EventPipeThread * ep_thread_get (void) { @@ -88,6 +116,26 @@ ep_thread_get_or_create (void) return ep_rt_thread_get_or_create (); } +void +ep_thread_get_threads (ep_rt_thread_array_t *threads) +{ + EP_ASSERT (threads != NULL); + + ep_rt_spin_lock_aquire (&_ep_threads_lock); + ep_rt_thread_array_iterator_t threads_iterator; + ep_rt_thread_array_iterator_begin (&_ep_threads, &threads_iterator); + while (!ep_rt_thread_array_iterator_end (&_ep_threads, &threads_iterator)) { + EventPipeThread *thread = ep_rt_thread_array_iterator_value (&threads_iterator); + if (thread) { + // Add ref so the thread doesn't disappear when we release the lock + ep_thread_addref (thread); + ep_rt_thread_array_append (threads, thread); + } + ep_rt_thread_array_iterator_next (&_ep_threads, &threads_iterator); + } + ep_rt_spin_lock_release (&_ep_threads_lock); +} + void ep_thread_create_activity_id ( uint8_t *activity_id, diff --git a/src/mono/mono/eventpipe/ep-thread.h b/src/mono/mono/eventpipe/ep-thread.h index c0a634a4981ff3..dc2aa27f64dd4b 100644 --- a/src/mono/mono/eventpipe/ep-thread.h +++ b/src/mono/mono/eventpipe/ep-thread.h @@ -72,12 +72,21 @@ ep_thread_addref (EventPipeThread *thread); void ep_thread_release (EventPipeThread *thread); +void +ep_thread_init (void); + +void +ep_thread_fini (void); + EventPipeThread * ep_thread_get (void); EventPipeThread * ep_thread_get_or_create (void); +void +ep_thread_get_threads (ep_rt_thread_array_t *threads); + void ep_thread_create_activity_id ( uint8_t *activity_id, diff --git a/src/mono/mono/eventpipe/ep-types.h b/src/mono/mono/eventpipe/ep-types.h index ed4570d60f2770..1a0c729a9c162a 100644 --- a/src/mono/mono/eventpipe/ep-types.h +++ b/src/mono/mono/eventpipe/ep-types.h @@ -145,7 +145,8 @@ typedef enum { typedef enum { EP_SESSION_TYPE_FILE, EP_SESSION_TYPE_LISTENER, - EP_SESSION_TYPE_IPCSTREAM + EP_SESSION_TYPE_IPCSTREAM, + EP_SESSION_TYPE_SYNCHRONOUS } EventPipeSessionType ; typedef enum { @@ -183,6 +184,20 @@ typedef void (*EventPipeCallbackDataFree)( EventPipeCallback callback, void *callback_data); +typedef void (*EventPipeSessionSynchronousCallback)( + EventPipeProvider *provider, + int32_t event_id, + int32_t event_version, + uint32_t metadata_blob_len, + const uint8_t *metadata_blob, + uint32_t event_data_len, + const uint8_t *event_data, + const uint8_t *activity_id, + const uint8_t *related_activity_id, + EventPipeThread *event_thread, + uint32_t stack_frames_len, + uintptr_t *stack_frames); + /* * EventFilterDescriptor. */ diff --git a/src/mono/mono/eventpipe/ep.c b/src/mono/mono/eventpipe/ep.c index d95010db061594..b29813ad140828 100644 --- a/src/mono/mono/eventpipe/ep.c +++ b/src/mono/mono/eventpipe/ep.c @@ -35,7 +35,10 @@ #include "ep-session.h" #endif -static bool _ep_enable_sample_profiler_at_startup = false; +static bool _ep_can_start_threads = false; + +static ep_rt_session_id_array_t _ep_deferred_enable_session_ids = { 0 }; +static ep_rt_session_id_array_t _ep_deferred_disable_session_ids = { 0 }; /* * Forward declares of all static functions. @@ -68,8 +71,8 @@ enable ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler, - EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeSessionSynchronousCallback sync_callback); static void @@ -84,9 +87,7 @@ disable_holding_lock ( static void -disable ( - EventPipeSessionID id, - EventPipeProviderCallbackDataQueue *provider_callback_data_queue); +disable (EventPipeSessionID id); static void @@ -127,6 +128,14 @@ static void enable_default_session_via_env_variables (void); +static +bool +session_requested_sampling (EventPipeSession *session); + +static +bool +ipc_stream_factory_any_suspended_ports (void); + /* * Global volatile varaibles, only to be accessed through inlined volatile access functions. */ @@ -398,11 +407,11 @@ enable ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler, - EventPipeProviderCallbackDataQueue *provider_callback_data_queue) + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeSessionSynchronousCallback sync_callback) { EP_ASSERT (format < EP_SERIALIZATION_FORMAT_COUNT); - EP_ASSERT (circular_buffer_size_in_mb > 0); + EP_ASSERT (session_type == EP_SESSION_TYPE_SYNCHRONOUS || circular_buffer_size_in_mb > 0); EP_ASSERT (providers_len > 0); EP_ASSERT (providers != NULL); EP_ASSERT ((session_type == EP_SESSION_TYPE_FILE && output_path != NULL) ||(session_type == EP_SESSION_TYPE_IPCSTREAM && stream != NULL)); @@ -428,7 +437,7 @@ enable ( circular_buffer_size_in_mb, providers, providers_len, - FALSE); + sync_callback); ep_raise_error_if_nok (session != NULL && ep_session_is_valid (session) == true); @@ -465,10 +474,7 @@ enable ( // Enable tracing. config_enable_disable (ep_config_get (), session, provider_callback_data_queue, true); - session = NULL; - - // Enable the sample profiler (if supported). - if (enable_sample_profiler) + if (session_requested_sampling (session)) ep_rt_sample_profiler_enable (); ep_on_exit: @@ -509,8 +515,10 @@ disable_holding_lock ( if (is_session_id_in_collection (id)) { EventPipeSession *const session = (EventPipeSession *)id; - // Disable the profiler. - ep_rt_sample_profiler_disable (); + if (session_requested_sampling (session)) { + // Disable the profiler. + ep_rt_sample_profiler_disable (); + } // Log the process information event. log_process_info_event (ep_event_source_get ()); @@ -521,7 +529,7 @@ disable_holding_lock ( ep_session_disable (session); // WriteAllBuffersToFile, and remove providers. // Do rundown before fully stopping the session unless rundown wasn't requested - if (ep_session_get_rundown_requested (session)) { + if (ep_session_get_rundown_requested (session) && _ep_can_start_threads) { ep_session_enable_rundown (session); // Set Rundown provider. EventPipeThread *const thread = ep_thread_get (); if (thread != NULL) { @@ -540,6 +548,13 @@ disable_holding_lock ( } ep_volatile_store_allow_write (ep_volatile_load_allow_write () & ~(ep_session_get_mask (session))); + + // Remove the session from the array before calling ep_session_suspend_write_event. This way + // we can guarantee that either the event write got the pointer and will complete + // the write successfully, or it gets NULL and will bail. + EP_ASSERT (ep_volatile_load_session (ep_session_get_index (session)) == session); + ep_volatile_store_session (ep_session_get_index (session), NULL); + ep_session_suspend_write_event (session); bool ignored; @@ -547,13 +562,6 @@ disable_holding_lock ( ep_volatile_store_number_of_sessions (ep_volatile_load_number_of_sessions () - 1); - // At this point, we should not be writing events to this session anymore - // This is a good time to remove the session from the array. - EP_ASSERT (ep_volatile_load_session (ep_session_get_index (session)) == session); - - // Remove the session from the array, and mask. - ep_volatile_store_session (ep_session_get_index (session), NULL); - // Write a final sequence point to the file now that all events have // been emitted. ep_session_write_sequence_point_unbuffered (session); @@ -570,15 +578,33 @@ disable_holding_lock ( static void -disable ( - EventPipeSessionID id, - EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +disable (EventPipeSessionID id) { ep_requires_lock_not_held (); - EP_LOCK_ENTER (section1) - disable_holding_lock (id, provider_callback_data_queue); - EP_LOCK_EXIT (section1) + if (_ep_can_start_threads) + ep_rt_thread_setup (); + + if (id == 0) + return; + + // Don't block GC during clean-up. + EP_GCX_PREEMP_ENTER + + EventPipeProviderCallbackDataQueue callback_data_queue; + EventPipeProviderCallbackData provider_callback_data; + EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&callback_data_queue); + + EP_LOCK_ENTER (section1) + disable_holding_lock (id, provider_callback_data_queue); + EP_LOCK_EXIT (section1) + + while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) + provider_invoke_callback (&provider_callback_data); + + ep_provider_callback_data_queue_fini (provider_callback_data_queue); + + EP_GCX_PREEMP_EXIT ep_on_exit: ep_requires_lock_not_held (); @@ -653,7 +679,7 @@ write_event_2 ( uint8_t *data = ep_event_payload_get_flat_data (payload); if (thread != NULL && rundown_session != NULL && data != NULL) { - ep_session_write_event_buffered ( + ep_session_write_event ( rundown_session, thread, ep_event, @@ -679,7 +705,7 @@ write_event_2 ( // Disable is allowed to set s_pSessions[i] = NULL at any time and that may have occured in between // the check and the load if (session != NULL) { - ep_session_write_event_buffered ( + ep_session_write_event ( session, thread, ep_event, @@ -779,7 +805,6 @@ enable_default_session_via_env_variables (void) ep_char8_t *ep_config_output_path = NULL; uint32_t ep_circular_mb = 0; const ep_char8_t *output_path = NULL; - EventPipeSessionProviderList *provider_list = NULL; if (ep_rt_config_value_get_enable ()) { ep_config = ep_rt_config_value_get_config (); @@ -798,14 +823,10 @@ enable_default_session_via_env_variables (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, true, NULL, - false); + NULL); - if (session_id) { - provider_list = ep_session_get_providers ((EventPipeSession *)session_id); - if (ep_rt_session_provider_list_find_by_name (ep_session_provider_list_get_providers_cref (provider_list), "Microsoft-DotNETCore-SampleProfiler")) - _ep_enable_sample_profiler_at_startup = true; + if (session_id) ep_start_streaming (session_id); - } } ep_rt_utf8_string_free (ep_config_output_path); @@ -813,6 +834,22 @@ enable_default_session_via_env_variables (void) return; } +static +bool +session_requested_sampling (EventPipeSession *session) +{ + EP_ASSERT (session != NULL); + return ep_rt_session_provider_list_find_by_name (ep_session_provider_list_get_providers_cref (ep_session_get_providers (session)), "Microsoft-DotNETCore-SampleProfiler"); +} + +// TODO: Replace with diagnostic server port implementation. +static +bool +ipc_stream_factory_any_suspended_ports (void) +{ + return false; +} + #ifdef EP_CHECKED_BUILD void ep_requires_lock_held (void) @@ -837,10 +874,10 @@ ep_enable ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler) + EventPipeSessionSynchronousCallback sync_callback) { ep_return_zero_if_nok (format < EP_SERIALIZATION_FORMAT_COUNT); - ep_return_zero_if_nok (circular_buffer_size_in_mb > 0); + ep_return_zero_if_nok (session_type == EP_SESSION_TYPE_SYNCHRONOUS || circular_buffer_size_in_mb > 0); ep_return_zero_if_nok (providers_len > 0 && providers != NULL); ep_requires_lock_not_held (); @@ -866,8 +903,8 @@ ep_enable ( format, rundown_requested, stream, - enable_sample_profiler, - provider_callback_data_queue); + provider_callback_data_queue, + sync_callback); EP_LOCK_EXIT (section1) while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) @@ -892,7 +929,7 @@ ep_enable_2 ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler) + EventPipeSessionSynchronousCallback sync_callback) { const ep_char8_t *providers_config_to_parse = providers_config; int32_t providers_len = 0; @@ -969,7 +1006,7 @@ ep_enable_2 ( format, rundown_requested, stream, - enable_sample_profiler); + sync_callback); ep_on_exit: @@ -994,29 +1031,29 @@ ep_disable (EventPipeSessionID id) { ep_requires_lock_not_held (); - ep_rt_thread_setup (); - - if (id == 0) - return; - - // Don't block GC during clean-up. - EP_GCX_PREEMP_ENTER - - EventPipeProviderCallbackDataQueue callback_data_queue; - EventPipeProviderCallbackData provider_callback_data; - EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&callback_data_queue); - - disable (id, provider_callback_data_queue); - - while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) - provider_invoke_callback (&provider_callback_data); - - ep_provider_callback_data_queue_fini (provider_callback_data_queue); + // ep_disable is called synchronously since the diagnostics server is + // single threaded. HOWEVER, if the runtime was suspended during startup, + // then ep_finish_init might not have executed yet. Disabling a session + // needs to either happen before we resume or after initialization. We briefly take the + // lock to check _ep_can_start_threads to check whether we've finished initialization. We + // also check whether we are still suspended in which case we can safely disable the session + // without deferral. + EP_LOCK_ENTER (section1) + if (!_ep_can_start_threads && !ipc_stream_factory_any_suspended_ports ()) + { + ep_rt_session_id_array_append (&_ep_deferred_disable_session_ids, id); + ep_raise_error_holding_lock (section1); + } + EP_LOCK_EXIT (section1) - EP_GCX_PREEMP_EXIT + disable (id); +ep_on_exit: ep_requires_lock_not_held (); return; + +ep_on_error: + ep_exit_error_handler (); } EventPipeSession * @@ -1044,6 +1081,13 @@ ep_get_session (EventPipeSessionID session_id) ep_exit_error_handler (); } +bool +ep_is_session_enabled (EventPipeSessionID session_id) +{ + ep_return_false_if_nok (session_id != 0); + return ep_volatile_load_session (ep_session_get_index ((EventPipeSession *)session_id)) != NULL; +} + void ep_start_streaming (EventPipeSessionID session_id) { @@ -1051,7 +1095,10 @@ ep_start_streaming (EventPipeSessionID session_id) EP_LOCK_ENTER (section1) ep_raise_error_if_nok_holding_lock (is_session_id_in_collection (session_id) == true, section1); - ep_session_start_streaming ((EventPipeSession *)session_id); + if (_ep_can_start_threads) + ep_session_start_streaming ((EventPipeSession *)session_id); + else + ep_rt_session_id_array_append (&_ep_deferred_enable_session_ids, session_id); EP_LOCK_EXIT (section1) ep_on_exit: @@ -1093,6 +1140,8 @@ ep_create_provider ( while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) provider_invoke_callback (&provider_callback_data); + ep_rt_notify_profiler_provider_created (provider); + ep_on_exit: ep_provider_callback_data_queue_fini (provider_callback_data_queue); ep_requires_lock_not_held (); @@ -1155,6 +1204,27 @@ ep_get_provider (const ep_char8_t *provider_name) ep_exit_error_handler (); } +void +ep_add_provider_to_session ( + EventPipeSessionProvider *provider, + EventPipeSession *session) +{ + ep_return_void_if_nok (provider != NULL && session != NULL); + + ep_requires_lock_not_held (); + + EP_LOCK_ENTER (section1) + ep_session_add_session_provider (session, provider); + EP_LOCK_EXIT (section1) + +ep_on_exit: + ep_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + void ep_init (void) { @@ -1167,6 +1237,8 @@ ep_init (void) return; } + ep_thread_init (); + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) ep_volatile_store_session (i, NULL); @@ -1180,7 +1252,10 @@ ep_init (void) // Set the sampling rate for the sample profiler. const uint32_t default_profiler_sample_rate_in_nanoseconds = 1000000; // 1 msec. - ep_rt_sample_set_sampling_rate (default_profiler_sample_rate_in_nanoseconds); + ep_rt_sample_profiler_set_sampling_rate (default_profiler_sample_rate_in_nanoseconds); + + ep_rt_session_id_array_alloc (&_ep_deferred_enable_session_ids); + ep_rt_session_id_array_alloc (&_ep_deferred_disable_session_ids); EP_LOCK_ENTER (section1) ep_volatile_store_eventpipe_state_without_barrier (EP_STATE_INITIALIZED); @@ -1199,8 +1274,47 @@ ep_init (void) void ep_finish_init (void) { - if (_ep_enable_sample_profiler_at_startup) - ep_rt_sample_profiler_enable (); + ep_requires_lock_not_held (); + + // Enable streaming for any deferred sessions + EP_LOCK_ENTER (section1) + _ep_can_start_threads = true; + if (ep_volatile_load_eventpipe_state_without_barrier () == EP_STATE_INITIALIZED) { + ep_rt_session_id_array_iterator_t deferred_session_ids_iterator; + ep_rt_session_id_array_iterator_begin (&_ep_deferred_enable_session_ids, &deferred_session_ids_iterator); + while (!ep_rt_session_id_array_iterator_end (&_ep_deferred_enable_session_ids, &deferred_session_ids_iterator)) { + EventPipeSessionID session_id = ep_rt_session_id_array_iterator_value (&deferred_session_ids_iterator); + if (is_session_id_in_collection (session_id)) + ep_session_start_streaming ((EventPipeSession *)session_id); + ep_rt_session_id_array_iterator_next (&_ep_deferred_enable_session_ids, &deferred_session_ids_iterator); + } + ep_rt_session_id_array_clear (&_ep_deferred_enable_session_ids); + } + + ep_rt_sample_profiler_can_start_sampling (); + EP_LOCK_EXIT (section1) + + // release lock in case someone tried to disable while we held it + // _ep_deferred_disable_session_ids is now safe to access without the + // lock since we've set _ep_can_start_threads to true inside the lock. Anyone + // who was waiting on that lock will see that state and not mutate the defer list + if (ep_volatile_load_eventpipe_state_without_barrier () == EP_STATE_INITIALIZED) { + ep_rt_session_id_array_iterator_t deferred_session_ids_iterator; + ep_rt_session_id_array_iterator_begin (&_ep_deferred_disable_session_ids, &deferred_session_ids_iterator); + while (!ep_rt_session_id_array_iterator_end (&_ep_deferred_disable_session_ids, &deferred_session_ids_iterator)) { + EventPipeSessionID session_id = ep_rt_session_id_array_iterator_value (&deferred_session_ids_iterator); + disable (session_id); + ep_rt_session_id_array_iterator_next (&_ep_deferred_disable_session_ids, &deferred_session_ids_iterator); + } + ep_rt_session_id_array_clear (&_ep_deferred_disable_session_ids); + } + +ep_on_exit: + ep_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); } void @@ -1222,6 +1336,11 @@ ep_shutdown (void) ep_disable ((EventPipeSessionID)session); } + ep_rt_session_id_array_free (&_ep_deferred_enable_session_ids); + ep_rt_session_id_array_free (&_ep_deferred_disable_session_ids); + + ep_thread_fini (); + // dotnet/coreclr: issue 24850: EventPipe shutdown race conditions // Deallocating providers/events here might cause AV if a WriteEvent // was to occur. Thus, we are not doing this cleanup. diff --git a/src/mono/mono/eventpipe/ep.h b/src/mono/mono/eventpipe/ep.h index 6f9b2cb71b400f..31e38137125020 100644 --- a/src/mono/mono/eventpipe/ep.h +++ b/src/mono/mono/eventpipe/ep.h @@ -182,7 +182,7 @@ ep_enable ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler); + EventPipeSessionSynchronousCallback sync_callback); EventPipeSessionID ep_enable_2 ( @@ -193,7 +193,7 @@ ep_enable_2 ( EventPipeSerializationFormat format, bool rundown_requested, IpcStream *stream, - bool enable_sample_profiler); + EventPipeSessionSynchronousCallback sync_callback); void ep_disable (EventPipeSessionID id); @@ -201,6 +201,9 @@ ep_disable (EventPipeSessionID id); EventPipeSession * ep_get_session (EventPipeSessionID session_id); +bool +ep_is_session_enabled (EventPipeSessionID session_id); + void ep_start_streaming (EventPipeSessionID session_id); @@ -220,6 +223,11 @@ ep_delete_provider (EventPipeProvider *provider); EventPipeProvider * ep_get_provider (const ep_char8_t *provider_name); +void +ep_add_provider_to_session ( + EventPipeSessionProvider *provider, + EventPipeSession *session); + void ep_init (void); @@ -278,6 +286,14 @@ ep_init (void) ; } +static +inline +void +ep_finish_init (void) +{ + ; +} + static inline void diff --git a/src/mono/mono/eventpipe/test/ep-session-tests.c b/src/mono/mono/eventpipe/test/ep-session-tests.c index 181574006aea19..5d1e2854cf8e8f 100644 --- a/src/mono/mono/eventpipe/test/ep-session-tests.c +++ b/src/mono/mono/eventpipe/test/ep-session-tests.c @@ -108,7 +108,10 @@ test_add_session_providers (void) test_location = 4; - ep_session_add_session_provider (test_session, test_session_provider); + EP_LOCK_ENTER (section2) + ep_session_add_session_provider (test_session, test_session_provider); + EP_LOCK_EXIT (section2) + test_session_provider = NULL; if (!ep_session_is_valid (test_session)) { diff --git a/src/mono/mono/eventpipe/test/ep-tests.c b/src/mono/mono/eventpipe/test/ep-tests.c index 3aeeb65ed6986a..5ae0b222155932 100644 --- a/src/mono/mono/eventpipe/test/ep-tests.c +++ b/src/mono/mono/eventpipe/test/ep-tests.c @@ -191,7 +191,7 @@ test_enable_disable (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -313,7 +313,7 @@ test_enable_disable_default_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -361,7 +361,7 @@ test_enable_disable_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -441,7 +441,7 @@ test_enable_disable_provider_parse_default_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -512,7 +512,7 @@ test_create_delete_provider_with_callback (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -613,7 +613,7 @@ test_session_start_streaming (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, - false); + NULL); if (!session_id) { result = FAILED ("Failed to enable session"); @@ -662,7 +662,7 @@ test_session_write_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -671,7 +671,7 @@ test_session_write_event (void) EventPipeEventPayload payload;; ep_event_payload_init (&payload, NULL, 0); - write_result = ep_session_write_event_buffered ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); + write_result = ep_session_write_event ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); ep_event_payload_fini (&payload); ep_raise_error_if_nok (write_result == true); @@ -715,7 +715,7 @@ test_session_write_event_seq_point (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -724,7 +724,7 @@ test_session_write_event_seq_point (void) EventPipeEventPayload payload;; ep_event_payload_init (&payload, NULL, 0); - write_result = ep_session_write_event_buffered ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); + write_result = ep_session_write_event ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); ep_event_payload_fini (&payload); ep_raise_error_if_nok (write_result == true); @@ -772,7 +772,7 @@ test_session_write_wait_get_next_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -781,7 +781,7 @@ test_session_write_wait_get_next_event (void) EventPipeEventPayload payload;; ep_event_payload_init (&payload, NULL, 0); - write_result = ep_session_write_event_buffered ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); + write_result = ep_session_write_event ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); ep_event_payload_fini (&payload); ep_raise_error_if_nok (write_result == true); @@ -853,7 +853,7 @@ test_session_write_get_next_event (void) EventPipeEventPayload payload;; ep_event_payload_init (&payload, NULL, 0); - write_result = ep_session_write_event_buffered ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); + write_result = ep_session_write_event ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); ep_event_payload_fini (&payload); ep_raise_error_if_nok (write_result == true); @@ -923,16 +923,14 @@ test_session_write_suspend_event (void) EventPipeEventPayload payload;; ep_event_payload_init (&payload, NULL, 0); - write_result = ep_session_write_event_buffered ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); + write_result = ep_session_write_event ((EventPipeSession *)session_id, ep_thread_get (), ep_event, &payload, NULL, NULL, NULL, NULL); ep_event_payload_fini (&payload); ep_raise_error_if_nok (write_result == true); test_location = 5; - EP_LOCK_ENTER (section1) - ep_session_suspend_write_event ((EventPipeSession *)session_id); - EP_LOCK_EXIT (section1) + // ep_session_suspend_write_event_happens in disable session. ep_on_exit: ep_disable (session_id); diff --git a/src/mono/mono/metadata/icall-eventpipe.c b/src/mono/mono/metadata/icall-eventpipe.c index 3763ae3578b551..ae86f89ac42650 100644 --- a/src/mono/mono/metadata/icall-eventpipe.c +++ b/src/mono/mono/metadata/icall-eventpipe.c @@ -320,7 +320,7 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Enable ( (EventPipeSerializationFormat)format, true, NULL, - true); + NULL); ep_start_streaming (session_id); if (config_providers) { diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 14ea80746a3aaf..403e1beef85df1 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4555,6 +4555,7 @@ mini_init (const char *filename, const char *runtime_version) #if defined(ENABLE_PERFTRACING) && !defined(DISABLE_EVENTPIPE) ep_init (); + ep_finish_init (); #endif if (mono_aot_only) {