Skip to content

Commit

Permalink
Move away from exposing internal EventPipe wait handle to EventPipeEv…
Browse files Browse the repository at this point in the history
…entDispatcher. (dotnet#68320)

* Move away from exposing internal EventPipe wait handle to EventPipeEventDispatcher.

Fix for issue, dotnet#68032.

EventPipe exposed and internal wait handle to EventPipeEventDispatcher.
The wait handle is not a managed WaitableObject, but maps to runtime
specific event types:

CLREventStatic * on CoreCLR
mono_w32event_create on Mono

Overtime it appears that the underlying WaitHandle changed on none Windows
platforms, so calls like:

EventWaitHandle.Set

Assumes that IntPtr used is a WaitableObject and on Mono this will cause
a crash, but on CoreCLR, it will just be casted over to a WaitableObject
and use it, so based on memory layout this could cause issue, but looking
at the implementation, it appears it will always assume EventWaitHandle
is signaled so won't touch any of the memory (but not signal the event).

There have been an ambition in DispatchEventsToEventListeners to move
this abstraction down into native code, get away from using a handle
returned from EventPipeInternal.GetWaitHandle as a EventWaitHandle. This
PR eliminate EventPipeInternal.GetWaitHandle and adds a SignalSession and
WaitForSessionSignal icall's (Mono), qcall's (CoreCLR) and fix the
issue caused by exposing the handle to managed code.
  • Loading branch information
lateralusX authored May 7, 2022
1 parent cfa18c9 commit e67e4da
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ private static unsafe partial ulong Enable(
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool GetNextEvent(ulong sessionID, EventPipeEventInstanceData* pInstance);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EventPipeInternal_GetWaitHandle")]
internal static unsafe partial IntPtr GetWaitHandle(ulong sessionID);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EventPipeInternal_SignalSession")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool SignalSession(ulong sessionID);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EventPipeInternal_WaitForSessionSignal")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool WaitForSessionSignal(ulong sessionID, int timeoutMs);
}
}

Expand Down
20 changes: 18 additions & 2 deletions src/coreclr/vm/eventpipeadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,26 @@ class EventPipeAdapter final
return ep_get_session(id);
}

static inline HANDLE GetWaitHandle(EventPipeSessionID id)
static inline bool SignalSession(EventPipeSessionID id)
{
STATIC_CONTRACT_NOTHROW;
return reinterpret_cast<HANDLE>(ep_get_wait_handle(id));

EventPipeSession *const session = ep_get_session (id);
if (!session)
return false;

return ep_rt_wait_event_set (ep_session_get_wait_event (session));
}

static inline bool WaitForSessionSignal(EventPipeSessionID id, INT32 timeoutMs)
{
STATIC_CONTRACT_NOTHROW;

EventPipeSession *const session = ep_get_session (id);
if (!session)
return false;

return !ep_rt_wait_event_wait (ep_session_get_wait_event (session), (uint32_t)timeoutMs, false) ? true : false;
}

static inline FILETIME GetSessionStartTime(EventPipeSession *session)
Expand Down
21 changes: 17 additions & 4 deletions src/coreclr/vm/eventpipeinternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,17 +255,30 @@ extern "C" bool QCALLTYPE EventPipeInternal_GetNextEvent(UINT64 sessionID, Event
return pNextInstance != NULL;
}

extern "C" HANDLE QCALLTYPE EventPipeInternal_GetWaitHandle(UINT64 sessionID)
extern "C" bool QCALLTYPE EventPipeInternal_SignalSession(UINT64 sessionID)
{
QCALL_CONTRACT;

HANDLE waitHandle = NULL;
bool result = false;
BEGIN_QCALL;

waitHandle = EventPipeAdapter::GetWaitHandle(sessionID);
result = EventPipeAdapter::SignalSession(sessionID);

END_QCALL;
return waitHandle;
return result;
}

extern "C" bool QCALLTYPE EventPipeInternal_WaitForSessionSignal(UINT64 sessionID, INT32 timeoutMs)
{
QCALL_CONTRACT;

bool result = false;
BEGIN_QCALL;

result = EventPipeAdapter::WaitForSessionSignal(sessionID, timeoutMs);

END_QCALL;
return result;
}

#endif // FEATURE_PERFTRACING
8 changes: 6 additions & 2 deletions src/coreclr/vm/eventpipeinternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ extern "C" void QCALLTYPE EventPipeInternal_DeleteProvider(
INT_PTR provHandle);

extern "C" int QCALLTYPE EventPipeInternal_EventActivityIdControl(
uint32_t controlCode,
UINT32 controlCode,
GUID *pActivityId);

extern "C" void QCALLTYPE EventPipeInternal_WriteEventData(
Expand All @@ -86,9 +86,13 @@ extern "C" bool QCALLTYPE EventPipeInternal_GetNextEvent(
UINT64 sessionID,
EventPipeEventInstanceData *pInstance);

extern "C" HANDLE QCALLTYPE EventPipeInternal_GetWaitHandle(
extern "C" bool QCALLTYPE EventPipeInternal_SignalSession(
UINT64 sessionID);

extern "C" bool QCALLTYPE EventPipeInternal_WaitForSessionSignal(
UINT64 sessionID,
INT32 timeoutMs);

#endif // FEATURE_PERFTRACING

#endif // __EVENTPIPEINTERNAL_H__
3 changes: 2 additions & 1 deletion src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ static const Entry s_QCall[] =
DllImportEntry(EventPipeInternal_GetProvider)
DllImportEntry(EventPipeInternal_WriteEventData)
DllImportEntry(EventPipeInternal_GetNextEvent)
DllImportEntry(EventPipeInternal_GetWaitHandle)
DllImportEntry(EventPipeInternal_SignalSession)
DllImportEntry(EventPipeInternal_WaitForSessionSignal)
#endif
#if defined(TARGET_UNIX)
DllImportEntry(CloseHandle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ internal EventListenerSubscription(EventKeywords matchAnyKeywords, EventLevel le
private long m_timeQPCFrequency;

private bool m_stopDispatchTask;
private readonly EventPipeWaitHandle m_dispatchTaskWaitHandle = new EventPipeWaitHandle();
private Task? m_dispatchTask;
private readonly object m_dispatchControlLock = new object();
private readonly Dictionary<EventListener, EventListenerSubscription> m_subscriptions = new Dictionary<EventListener, EventListenerSubscription>();
Expand All @@ -43,7 +42,6 @@ private EventPipeEventDispatcher()
{
// Get the ID of the runtime provider so that it can be used as a filter when processing events.
m_RuntimeProviderID = EventPipeInternal.GetProvider(NativeRuntimeEventSource.EventSourceName);
m_dispatchTaskWaitHandle.SafeWaitHandle = new SafeWaitHandle(IntPtr.Zero, false);
}

internal void SendCommand(EventListener eventListener, EventCommand command, bool enable, EventLevel level, EventKeywords matchAnyKeywords)
Expand Down Expand Up @@ -139,9 +137,6 @@ private void StartDispatchTask()
if (m_dispatchTask == null)
{
m_stopDispatchTask = false;
// Create a SafeWaitHandle that won't release the handle when done
m_dispatchTaskWaitHandle.SafeWaitHandle = new SafeWaitHandle(EventPipeInternal.GetWaitHandle(m_sessionID), false);

m_dispatchTask = Task.Factory.StartNew(DispatchEventsToEventListeners, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
Expand All @@ -153,8 +148,7 @@ private void StopDispatchTask()
if (m_dispatchTask != null)
{
m_stopDispatchTask = true;
Debug.Assert(!m_dispatchTaskWaitHandle.SafeWaitHandle.IsInvalid);
EventWaitHandle.Set(m_dispatchTaskWaitHandle.SafeWaitHandle);
EventPipeInternal.SignalSession(m_sessionID);
m_dispatchTask.Wait();
m_dispatchTask = null;
}
Expand Down Expand Up @@ -188,9 +182,7 @@ private unsafe void DispatchEventsToEventListeners()
{
if (!eventsReceived)
{
// Future TODO: this would make more sense to handle in EventPipeSession/EventPipe native code.
Debug.Assert(!m_dispatchTaskWaitHandle.SafeWaitHandle.IsInvalid);
m_dispatchTaskWaitHandle.WaitOne();
EventPipeInternal.WaitForSessionSignal(m_sessionID, Timeout.Infinite);
}

Thread.Sleep(10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ internal static unsafe IntPtr GetProvider(string providerName)
internal static extern unsafe bool GetNextEvent(ulong sessionID, EventPipeEventInstanceData* pInstance);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern unsafe IntPtr GetWaitHandle(ulong sessionID);
internal static extern unsafe bool SignalSession(ulong sessionID);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern unsafe bool WaitForSessionSignal(ulong sessionID, int timeoutMs);
#endif // FEATURE_PERFTRACING

//
Expand Down
26 changes: 25 additions & 1 deletion src/mono/mono/component/event_pipe-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ event_pipe_stub_write_event_threadpool_io_pack (
intptr_t overlapped,
uint16_t clr_instance_id);

static bool
event_pipe_stub_signal_session (EventPipeSessionID session_id);

static bool
event_pipe_stub_wait_for_session_signal (
EventPipeSessionID session_id,
uint32_t timeout);

MonoComponentEventPipe *
component_event_pipe_stub_init (void);

Expand Down Expand Up @@ -210,7 +218,9 @@ static MonoComponentEventPipe fn_table = {
&event_pipe_stub_write_event_threadpool_io_enqueue,
&event_pipe_stub_write_event_threadpool_io_dequeue,
&event_pipe_stub_write_event_threadpool_working_thread_count,
&event_pipe_stub_write_event_threadpool_io_pack
&event_pipe_stub_write_event_threadpool_io_pack,
&event_pipe_stub_signal_session,
&event_pipe_stub_wait_for_session_signal
};

static bool
Expand Down Expand Up @@ -459,6 +469,20 @@ event_pipe_stub_write_event_threadpool_io_pack (
return true;
}

static bool
event_pipe_stub_signal_session (EventPipeSessionID session_id)
{
return true;
}

static bool
event_pipe_stub_wait_for_session_signal (
EventPipeSessionID session_id,
uint32_t timeout)
{
return true;
}

MonoComponentEventPipe *
component_event_pipe_stub_init (void)
{
Expand Down
36 changes: 34 additions & 2 deletions src/mono/mono/component/event_pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ event_pipe_thread_ctrl_activity_id(
uint8_t *activity_id,
uint32_t activity_id_len);

static bool
event_pipe_signal_session (EventPipeSessionID session_id);

static bool
event_pipe_wait_for_session_signal (
EventPipeSessionID session_id,
uint32_t timeout);

static MonoComponentEventPipe fn_table = {
{ MONO_COMPONENT_ITF_VERSION, &event_pipe_available },
&ep_init,
Expand Down Expand Up @@ -114,7 +122,9 @@ static MonoComponentEventPipe fn_table = {
&ep_rt_write_event_threadpool_io_enqueue,
&ep_rt_write_event_threadpool_io_dequeue,
&ep_rt_write_event_threadpool_working_thread_count,
&ep_rt_write_event_threadpool_io_pack
&ep_rt_write_event_threadpool_io_pack,
&event_pipe_signal_session,
&event_pipe_wait_for_session_signal
};

static bool
Expand Down Expand Up @@ -161,7 +171,7 @@ event_pipe_enable (
rundown_requested,
stream,
sync_callback,
NULL);
NULL);

if (config_providers) {
for (int i = 0; i < providers_len; ++i) {
Expand Down Expand Up @@ -285,6 +295,28 @@ event_pipe_thread_ctrl_activity_id (
return result;
}

static bool
event_pipe_signal_session (EventPipeSessionID session_id)
{
EventPipeSession *const session = ep_get_session (session_id);
if (!session)
return false;

return ep_rt_wait_event_set (ep_session_get_wait_event (session));
}

static bool
event_pipe_wait_for_session_signal (
EventPipeSessionID session_id,
uint32_t timeout)
{
EventPipeSession *const session = ep_get_session (session_id);
if (!session)
return false;

return !ep_rt_wait_event_wait (ep_session_get_wait_event (session), timeout, false) ? true : false;
}

MonoComponentEventPipe *
mono_component_event_pipe_init (void)
{
Expand Down
10 changes: 10 additions & 0 deletions src/mono/mono/component/event_pipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ typedef bool
typedef ep_timestamp_t
(*event_pipe_component_convert_100ns_ticks_to_timestamp_t_func) (int64_t ticks_100ns);

typedef bool
(*event_pipe_component_signal_session) (EventPipeSessionID session_id);

typedef bool
(*event_pipe_component_wait_for_session_signal) (
EventPipeSessionID session_id,
uint32_t timeout);

/*
* EventPipeProvider.
*/
Expand Down Expand Up @@ -242,6 +250,8 @@ typedef struct _MonoComponentEventPipe {
event_pipe_component_write_event_threadpool_io_dequeue_func write_event_threadpool_io_dequeue;
event_pipe_component_write_event_threadpool_working_thread_count_func write_event_threadpool_working_thread_count;
event_pipe_component_write_event_threadpool_io_pack_func write_event_threadpool_io_pack;
event_pipe_component_signal_session signal_session;
event_pipe_component_wait_for_session_signal wait_for_session_signal;
} MonoComponentEventPipe;

MONO_COMPONENT_EXPORT_ENTRYPOINT
Expand Down
3 changes: 2 additions & 1 deletion src/mono/mono/metadata/icall-decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_
ICALL_EXPORT intptr_t ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider (const_gunichar2_ptr provider_name);
ICALL_EXPORT guint64 ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetRuntimeCounterValue (gint32 counter);
ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo (uint64_t session_id, void *session_info);
ICALL_EXPORT intptr_t ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id);
ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_SignalSession (uint64_t session_id);
ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WaitForSessionSignal (uint64_t session_id, int32_t timeout);
ICALL_EXPORT void ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData (intptr_t event_handle, void *event_data, uint32_t event_data_len, const uint8_t *activity_id, const uint8_t *related_activity_id);

ICALL_EXPORT void ves_icall_System_Diagnostics_Tracing_NativeRuntimeEventSource_LogThreadPoolIODequeue (intptr_t native_overlapped, intptr_t overlapped, uint16_t clr_instance_id);
Expand Down
5 changes: 3 additions & 2 deletions src/mono/mono/metadata/icall-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ NOHANDLES(ICALL(EVENTPIPE_7, "GetNextEvent", ves_icall_System_Diagnostics_Tracin
NOHANDLES(ICALL(EVENTPIPE_8, "GetProvider", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider))
NOHANDLES(ICALL(EVENTPIPE_9, "GetRuntimeCounterValue", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetRuntimeCounterValue))
NOHANDLES(ICALL(EVENTPIPE_10, "GetSessionInfo", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo))
NOHANDLES(ICALL(EVENTPIPE_11, "GetWaitHandle", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle))
NOHANDLES(ICALL(EVENTPIPE_12, "WriteEventData", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData))
NOHANDLES(ICALL(EVENTPIPE_12, "SignalSession", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_SignalSession))
NOHANDLES(ICALL(EVENTPIPE_14, "WaitForSessionSignal", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WaitForSessionSignal))
NOHANDLES(ICALL(EVENTPIPE_13, "WriteEventData", ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData))

ICALL_TYPE(NATIVE_RUNTIME_EVENT_SOURCE, "System.Diagnostics.Tracing.NativeRuntimeEventSource", NATIVE_RUNTIME_EVENT_SOURCE_1)
NOHANDLES(ICALL(NATIVE_RUNTIME_EVENT_SOURCE_1, "LogThreadPoolIODequeue", ves_icall_System_Diagnostics_Tracing_NativeRuntimeEventSource_LogThreadPoolIODequeue))
Expand Down
33 changes: 26 additions & 7 deletions src/mono/mono/metadata/icall-eventpipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,18 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo (
return mono_component_event_pipe()->get_session_info (session_id, (EventPipeSessionInfo *)session_info) ? TRUE : FALSE;
}

intptr_t
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id)
MonoBoolean
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_SignalSession (uint64_t session_id)
{
return (intptr_t) mono_component_event_pipe()->signal_session ((EventPipeSessionID)session_id);
}

MonoBoolean
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WaitForSessionSignal (
uint64_t session_id,
int32_t timeout)
{
return (intptr_t) mono_component_event_pipe()->get_wait_handle ((EventPipeSessionID)session_id);
return (intptr_t) mono_component_event_pipe()->wait_for_session_signal ((EventPipeSessionID)session_id, (uint32_t)timeout);
}

void
Expand Down Expand Up @@ -577,13 +585,24 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo (
return FALSE;
}

intptr_t
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id)
MonoBoolean
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_SignalSession (uint64_t session_id)
{
ERROR_DECL (error);
mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.GetWaitHandle");
mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.SignalSession");
mono_error_set_pending_exception (error);
return 0;
return FALSE;
}

MonoBoolean
ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WaitForSessionSignal (
uint64_t session_id,
int32_t timeout)
{
ERROR_DECL (error);
mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.WaitForSessionSignal");
mono_error_set_pending_exception (error);
return FALSE;
}

void
Expand Down
Loading

0 comments on commit e67e4da

Please sign in to comment.