Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[MonoVM] Add the ability for profilers to listen to EventPipe events.
Mono port of CoreCLR PR: #37002.
  • Loading branch information
lateralusX committed Aug 21, 2020
commit 71a1dca35b394aeaf2e064172095912268c89037
93 changes: 9 additions & 84 deletions src/mono/mono/eventpipe/ep-buffer-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -977,24 +962,13 @@ 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.
// This is the second time if this thread did have one or more buffers, but they were full.
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)
}
}
Expand Down Expand Up @@ -1034,73 +1008,33 @@ 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)) {
EventPipeThread *thread = ep_rt_thread_array_iterator_value (&thread_array_iterator);
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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion src/mono/mono/eventpipe/ep-buffer-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/eventpipe/ep-event-source.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono/eventpipe/ep-event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 24 additions & 1 deletion src/mono/mono/eventpipe/ep-rt-mono.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@
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) \
Expand Down Expand Up @@ -621,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.
}
Expand Down
11 changes: 10 additions & 1 deletion src/mono/mono/eventpipe/ep-rt.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
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) \
Expand Down Expand Up @@ -231,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.
Expand Down
Loading