diff options
author | Brian Robbins <brianrob@microsoft.com> | 2018-08-01 11:38:39 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-01 11:38:39 -0700 |
commit | c4c16723a83c995838508ed7bca7fb99687f6d40 (patch) | |
tree | 353e30ee3936156ca831264a2292a248af06d825 /src | |
parent | 2293351dc5eeb00681c38b3c1e5975b4d1ce3609 (diff) | |
download | coreclr-c4c16723a83c995838508ed7bca7fb99687f6d40.tar.gz coreclr-c4c16723a83c995838508ed7bca7fb99687f6d40.tar.bz2 coreclr-c4c16723a83c995838508ed7bca7fb99687f6d40.zip |
Expose OSThreadId and TimeStamp on EventWrittenEventArgs (#19002)
* Add EventWrittenEventArgs public properties OSThreadId and TimeStamp and plumb through OSThreadId.
* Plumb ActivityId and RelatedActivityId to EventListener.
Diffstat (limited to 'src')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs | 63 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs | 13 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs | 10 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs | 29 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs | 52 | ||||
-rw-r--r-- | src/pal/inc/pal.h | 6 | ||||
-rw-r--r-- | src/pal/src/thread/thread.cpp | 25 | ||||
-rw-r--r-- | src/vm/comcallablewrapper.cpp | 30 | ||||
-rw-r--r-- | src/vm/comsynchronizable.cpp | 21 | ||||
-rw-r--r-- | src/vm/comsynchronizable.h | 1 | ||||
-rw-r--r-- | src/vm/ecalllist.h | 2 | ||||
-rw-r--r-- | src/vm/eventpipe.cpp | 75 | ||||
-rw-r--r-- | src/vm/eventpipe.h | 29 | ||||
-rw-r--r-- | src/vm/eventpipesession.cpp | 2 | ||||
-rw-r--r-- | src/vm/eventpipesession.h | 20 |
15 files changed, 335 insertions, 43 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs index 6ee5f25f84..9ebb1030b8 100644 --- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs @@ -1913,13 +1913,25 @@ namespace System.Diagnostics.Tracing // Maintain old behavior - object identity is preserved if (AppContextSwitches.PreserveEventListnerObjectIdentity) { - WriteToAllListeners(eventId, pActivityId, childActivityID, args); + WriteToAllListeners( + eventId: eventId, + osThreadId: null, + timeStamp: null, + activityID: pActivityId, + childActivityID: childActivityID, + args: args); } else #endif // !ES_BUILD_STANDALONE { object[] serializedArgs = SerializeEventArgs(eventId, args); - WriteToAllListeners(eventId, pActivityId, childActivityID, serializedArgs); + WriteToAllListeners( + eventId: eventId, + osThreadId: null, + timeStamp: null, + activityID: pActivityId, + childActivityID: childActivityID, + args: serializedArgs); } } } @@ -2021,14 +2033,24 @@ namespace System.Diagnostics.Tracing EventSource.EventData* dataPtr = data; for (int i = 0; i < paramCount; i++) args[i] = DecodeObject(eventId, i, ref dataPtr); - WriteToAllListeners(eventId, activityID, childActivityID, args); + WriteToAllListeners( + eventId: eventId, + osThreadId: null, + timeStamp: null, + activityID: activityID, + childActivityID: childActivityID, + args: args); } // helper for writing to all EventListeners attached the current eventSource. - internal unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, params object[] args) + internal unsafe void WriteToAllListeners(int eventId, uint* osThreadId, DateTime* timeStamp, Guid* activityID, Guid* childActivityID, params object[] args) { EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this); eventCallbackArgs.EventId = eventId; + if (osThreadId != null) + eventCallbackArgs.OSThreadId = (int)*osThreadId; + if (timeStamp != null) + eventCallbackArgs.TimeStamp = *timeStamp; if (activityID != null) eventCallbackArgs.ActivityId = *activityID; if (childActivityID != null) @@ -4609,16 +4631,47 @@ namespace System.Diagnostics.Tracing } } -#region private + /// <summary> + /// Gets the identifier for the OS thread that wrote the event. + /// </summary> + public long OSThreadId + { + get + { + if (!m_osThreadId.HasValue) + { + m_osThreadId = (long)RuntimeThread.CurrentOSThreadId; + } + + return m_osThreadId.Value; + } + internal set + { + m_osThreadId = value; + } + } + + /// <summary> + /// Gets a UTC DateTime that specifies when the event was written. + /// </summary> + public DateTime TimeStamp + { + get; + internal set; + } + + #region private internal EventWrittenEventArgs(EventSource eventSource) { m_eventSource = eventSource; + TimeStamp = DateTime.UtcNow; } private string m_message; private string m_eventName; private EventSource m_eventSource; private ReadOnlyCollection<string> m_payloadNames; private Guid m_activityId; + private long? m_osThreadId; internal EventTags m_tags; internal EventOpcode m_opcode; internal EventLevel m_level; diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs index 9a2204bf24..c613b2c01b 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs @@ -88,6 +88,19 @@ namespace Internal.Runtime.Augments private extern void SetPriorityNative(int priority); /*========================================================================= + ** Returns the operating system identifier for the current thread. + =========================================================================*/ + internal static ulong CurrentOSThreadId + { + get + { + return GetCurrentOSThreadId(); + } + } + [DllImport(JitHelpers.QCall)] + private static extern ulong GetCurrentOSThreadId(); + + /*========================================================================= ** Return the thread state as a consistent set of bits. This is more ** general then IsAlive or IsBackground. =========================================================================*/ diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs index 8bfac2b04b..42cf77959b 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs @@ -16,7 +16,7 @@ namespace System.Diagnostics.Tracing /// <param name="eventID">The eventID corresponding to the event as defined in the auto-generated portion of the RuntimeEventSource class.</param> /// <param name="payload">A span pointing to the data payload for the event.</param> [NonEvent] - internal unsafe void ProcessEvent(uint eventID, ReadOnlySpan<Byte> payload) + internal unsafe void ProcessEvent(uint eventID, uint osThreadID, DateTime timeStamp, Guid activityId, Guid childActivityId, ReadOnlySpan<Byte> payload) { // Make sure the eventID is valid. if (eventID >= m_eventData.Length) @@ -26,7 +26,13 @@ namespace System.Diagnostics.Tracing // Decode the payload. object[] decodedPayloadFields = EventPipePayloadDecoder.DecodePayload(ref m_eventData[eventID], payload); - WriteToAllListeners((int)eventID, null, null, decodedPayloadFields); + WriteToAllListeners( + eventId: (int)eventID, + osThreadId: &osThreadID, + timeStamp: &timeStamp, + activityID: &activityId, + childActivityID: &childActivityId, + args: decodedPayloadFields); } } #endif // FEATURE_PERFTRACING diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs index 89528cfd30..94ace3cca5 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs @@ -16,11 +16,23 @@ namespace System.Diagnostics.Tracing { internal IntPtr ProviderID; internal uint EventID; + internal uint ThreadID; + internal Int64 TimeStamp; + internal Guid ActivityId; + internal Guid ChildActivityId; internal IntPtr Payload; internal uint PayloadLength; } [StructLayout(LayoutKind.Sequential)] + internal struct EventPipeSessionInfo + { + internal Int64 StartTimeAsUTCFileTime; + internal Int64 StartTimeStamp; + internal Int64 TimeStampFrequency; + } + + [StructLayout(LayoutKind.Sequential)] internal struct EventPipeProviderConfiguration { [MarshalAs(UnmanagedType.LPWStr)] @@ -128,6 +140,8 @@ namespace System.Diagnostics.Tracing internal static class EventPipe { + private static UInt64 s_sessionID = 0; + internal static void Enable(EventPipeConfiguration configuration) { if(configuration == null) @@ -142,7 +156,7 @@ namespace System.Diagnostics.Tracing EventPipeProviderConfiguration[] providers = configuration.Providers; - EventPipeInternal.Enable( + s_sessionID = EventPipeInternal.Enable( configuration.OutputFile, configuration.CircularBufferSizeInMB, configuration.ProfilerSamplingRateInNanoseconds, @@ -152,7 +166,7 @@ namespace System.Diagnostics.Tracing internal static void Disable() { - EventPipeInternal.Disable(); + EventPipeInternal.Disable(s_sessionID); } } @@ -162,10 +176,10 @@ namespace System.Diagnostics.Tracing // These PInvokes are used by the configuration APIs to interact with EventPipe. // [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern void Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders); + internal static extern UInt64 Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern void Disable(); + internal static extern void Disable(UInt64 sessionID); // // These PInvokes are used by EventSource to interact with the EventPipe. @@ -191,6 +205,13 @@ namespace System.Diagnostics.Tracing [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern unsafe void WriteEventData(IntPtr eventHandle, uint eventID, EventProvider.EventData* pEventData, uint dataCount, Guid* activityId, Guid* relatedActivityId); + + // + // These PInvokes are used as part of the EventPipeEventDispatcher. + // + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern unsafe bool GetSessionInfo(UInt64 sessionID, EventPipeSessionInfo* pSessionInfo); + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern unsafe bool GetNextEvent(EventPipeEventInstanceData* pInstance); } diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs index 7d08d0db84..f7afd973c1 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs @@ -26,6 +26,11 @@ namespace System.Diagnostics.Tracing private IntPtr m_RuntimeProviderID; + private UInt64 m_sessionID = 0; + private DateTime m_syncTimeUtc; + private Int64 m_syncTimeQPC; + private Int64 m_timeQPCFrequency; + private bool m_stopDispatchTask; private Task m_dispatchTask = null; private object m_dispatchControlLock = new object(); @@ -73,13 +78,15 @@ namespace System.Diagnostics.Tracing private void CommitDispatchConfiguration() { + Debug.Assert(Monitor.IsEntered(m_dispatchControlLock)); + // Ensure that the dispatch task is stopped. // This is a no-op if the task is already stopped. StopDispatchTask(); // Stop tracing. // This is a no-op if it's already disabled. - EventPipeInternal.Disable(); + EventPipeInternal.Disable(m_sessionID); // Check to see if tracing should be enabled. if (m_subscriptions.Count <= 0) @@ -87,7 +94,7 @@ namespace System.Diagnostics.Tracing return; } - // Start collecting events. + // Determine the keywords and level that should be used based on the set of enabled EventListeners. EventKeywords aggregatedKeywords = EventKeywords.None; EventLevel highestLevel = EventLevel.LogAlways; @@ -97,12 +104,28 @@ namespace System.Diagnostics.Tracing highestLevel = (subscription.Level > highestLevel) ? subscription.Level : highestLevel; } + // Enable the EventPipe session. EventPipeProviderConfiguration[] providerConfiguration = new EventPipeProviderConfiguration[] { new EventPipeProviderConfiguration(RuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel) }; - EventPipeInternal.Enable(null, 1024, 1, providerConfiguration, 1); + m_sessionID = EventPipeInternal.Enable(null, 1024, 1, providerConfiguration, 1); + Debug.Assert(m_sessionID != 0); + + // Get the session information that is required to properly dispatch events. + EventPipeSessionInfo sessionInfo; + unsafe + { + if (!EventPipeInternal.GetSessionInfo(m_sessionID, &sessionInfo)) + { + Debug.Assert(false, "GetSessionInfo returned false."); + } + } + + m_syncTimeUtc = DateTime.FromFileTimeUtc(sessionInfo.StartTimeAsUTCFileTime); + m_syncTimeQPC = sessionInfo.StartTimeStamp; + m_timeQPCFrequency = sessionInfo.TimeStampFrequency; // Start the dispatch task. StartDispatchTask(); @@ -146,7 +169,8 @@ namespace System.Diagnostics.Tracing { // Dispatch the event. ReadOnlySpan<Byte> payload = new ReadOnlySpan<byte>((void*)instanceData.Payload, (int)instanceData.PayloadLength); - RuntimeEventSource.Log.ProcessEvent(instanceData.EventID, payload); + DateTime dateTimeStamp = TimeStampToDateTime(instanceData.TimeStamp); + RuntimeEventSource.Log.ProcessEvent(instanceData.EventID, instanceData.ThreadID, dateTimeStamp, instanceData.ActivityId, instanceData.ChildActivityId, payload); } } @@ -157,6 +181,26 @@ namespace System.Diagnostics.Tracing } } } + + /// <summary> + /// Converts a QueryPerformanceCounter (QPC) timestamp to a UTC DateTime. + /// </summary> + private DateTime TimeStampToDateTime(Int64 timeStamp) + { + if (timeStamp == Int64.MaxValue) + { + return DateTime.MaxValue; + } + + Debug.Assert((m_syncTimeUtc.Ticks != 0) && (m_syncTimeQPC != 0) && (m_timeQPCFrequency != 0)); + Int64 inTicks = (Int64)((timeStamp - m_syncTimeQPC) * 10000000.0 / m_timeQPCFrequency) + m_syncTimeUtc.Ticks; + if((inTicks < 0)|| (DateTime.MaxTicks < inTicks)) + { + inTicks = DateTime.MaxTicks; + } + + return new DateTime(inTicks, DateTimeKind.Utc); + } } #endif // FEATURE_PERFTRACING } diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 98d0ae2f11..993d76ed9e 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -1267,6 +1267,12 @@ PALAPI GetCurrentThreadId( VOID); +PALIMPORT +size_t +PALAPI +PAL_GetCurrentOSThreadId( + VOID); + // To work around multiply-defined symbols in the Carbon framework. #define GetCurrentThread PAL_GetCurrentThread PALIMPORT diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index bc06d2f7d4..66311edb03 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -399,6 +399,31 @@ GetCurrentThreadId( return dwThreadId; } +/*++ +Function: + PAL_GetCurrentOSThreadId + +Returns the current thread's OS thread ID. +This API is functionally equivalent to GetCurrentThreadId, but does not truncate the return value to 32-bits. +This is needed to ensure that we can provide the correct OS thread ID on platforms such as OSX that have a 64-bit thread ID. +--*/ +size_t +PALAPI +PAL_GetCurrentOSThreadId( + VOID) +{ + size_t threadId; + + PERF_ENTRY(PAL_GetCurrentOSThreadId); + ENTRY("PAL_GetCurrentOSThreadId()\n"); + + threadId = THREADSilentGetCurrentThreadId(); + + LOGEXIT("PAL_GetCurrentOSThreadId returns %p\n", threadId); + PERF_EXIT(GetCurrentThreadId); + + return threadId; +} /*++ diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index 15e02a214b..a2186a78b1 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -958,18 +958,24 @@ void SimpleComCallWrapper::BuildRefCountLogMessage(LPCWSTR wszOperation, StackSS if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, CCWRefCountChange)) { - SString className; - className.SetUTF8(pszClassName); - SString nameSpace; - nameSpace.SetUTF8(pszNamespace); - - FireEtwCCWRefCountChange( - handle, - (Object *)obj, - this, - dwEstimatedRefCount, - NULL, // domain value is not interesting in CoreCLR - className.GetUnicode(), nameSpace.GetUnicode(), wszOperation, GetClrInstanceId()); + EX_TRY + { + SString className; + className.SetUTF8(pszClassName); + SString nameSpace; + nameSpace.SetUTF8(pszNamespace); + + FireEtwCCWRefCountChange( + handle, + (Object *)obj, + this, + dwEstimatedRefCount, + NULL, // domain value is not interesting in CoreCLR + className.GetUnicode(), nameSpace.GetUnicode(), wszOperation, GetClrInstanceId()); + } + EX_CATCH + { } + EX_END_CATCH(SwallowAllExceptions); } if (g_pConfig->ShouldLogCCWRefCountChange(pszClassName, pszNamespace)) diff --git a/src/vm/comsynchronizable.cpp b/src/vm/comsynchronizable.cpp index a476a77609..0a6447bd9b 100644 --- a/src/vm/comsynchronizable.cpp +++ b/src/vm/comsynchronizable.cpp @@ -804,6 +804,27 @@ FCIMPL0(Object*, ThreadNative::GetCurrentThread) } FCIMPLEND +UINT64 QCALLTYPE ThreadNative::GetCurrentOSThreadId() +{ + QCALL_CONTRACT; + + // The Windows API GetCurrentThreadId returns a 32-bit integer thread ID. + // On some non-Windows platforms (e.g. OSX), the thread ID is a 64-bit value. + // We special case the API for non-Windows to get the 64-bit value and zero-extend + // the Windows value to return a single data type on all platforms. + + UINT64 threadId; + + BEGIN_QCALL; +#ifndef FEATURE_PAL + threadId = (UINT64) GetCurrentThreadId(); +#else + threadId = (UINT64) PAL_GetCurrentOSThreadId(); +#endif + END_QCALL; + + return threadId; +} FCIMPL3(void, ThreadNative::SetStart, ThreadBaseObject* pThisUNSAFE, Object* pDelegateUNSAFE, INT32 iRequestedStackSize) { diff --git a/src/vm/comsynchronizable.h b/src/vm/comsynchronizable.h index 87321fd336..ddd55cc21f 100644 --- a/src/vm/comsynchronizable.h +++ b/src/vm/comsynchronizable.h @@ -101,6 +101,7 @@ public: static FCDECL1(void, SpinWait, int iterations); static BOOL QCALLTYPE YieldThread(); static FCDECL0(Object*, GetCurrentThread); + static UINT64 QCALLTYPE GetCurrentOSThreadId(); static FCDECL1(void, Finalize, ThreadBaseObject* pThis); #ifdef FEATURE_COMINTEROP static FCDECL1(void, DisableComObjectEagerCleanup, ThreadBaseObject* pThis); diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 25bc85abce..46ee7f8c5f 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -668,6 +668,7 @@ FCFuncStart(gRuntimeThreadFuncs) FCFuncElement("get_IsThreadPoolThread", ThreadNative::IsThreadpoolThread) FCFuncElement("GetPriorityNative", ThreadNative::GetPriority) FCFuncElement("SetPriorityNative", ThreadNative::SetPriority) + QCFuncElement("GetCurrentOSThreadId", ThreadNative::GetCurrentOSThreadId) FCFuncElement("GetThreadStateNative", ThreadNative::GetThreadState) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT FCFuncElement("GetApartmentStateNative", ThreadNative::GetApartmentState) @@ -1154,6 +1155,7 @@ FCFuncEnd() FCFuncStart(gEventPipeInternalFuncs) QCFuncElement("Enable", EventPipeInternal::Enable) QCFuncElement("Disable", EventPipeInternal::Disable) + QCFuncElement("GetSessionInfo", EventPipeInternal::GetSessionInfo) QCFuncElement("CreateProvider", EventPipeInternal::CreateProvider) QCFuncElement("DefineEvent", EventPipeInternal::DefineEvent) QCFuncElement("DeleteProvider", EventPipeInternal::DeleteProvider) diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index 955c757790..42eea423a7 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -250,7 +250,7 @@ void EventPipe::Shutdown() // We are shutting down, so if disabling EventPipe throws, we need to move along anyway. EX_TRY { - Disable(); + Disable((EventPipeSessionID)s_pSession); } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); @@ -279,7 +279,7 @@ void EventPipe::Shutdown() #endif } -void EventPipe::Enable( +EventPipeSessionID EventPipe::Enable( LPCWSTR strOutputPath, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, @@ -301,10 +301,10 @@ void EventPipe::Enable( static_cast<unsigned int>(numProviders)); // Enable the session. - Enable(strOutputPath, pSession); + return Enable(strOutputPath, pSession); } -void EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) +EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) { CONTRACTL { @@ -318,13 +318,13 @@ void EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) // If tracing is not initialized or is already enabled, bail here. if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) { - return; + return 0; } // If the state or arguments are invalid, bail here. if(pSession == NULL || !pSession->IsValid()) { - return; + return 0; } // Enable the EventPipe EventSource. @@ -365,9 +365,12 @@ void EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) // Enable the sample profiler SampleProfiler::Enable(); + + // Return the session ID. + return (EventPipeSessionID)s_pSession; } -void EventPipe::Disable() +void EventPipe::Disable(EventPipeSessionID id) { CONTRACTL { @@ -377,6 +380,13 @@ void EventPipe::Disable() } CONTRACTL_END; + // Only perform the disable operation if the session ID + // matches the current active session. + if(id != (EventPipeSessionID)s_pSession) + { + return; + } + // Don't block GC during clean-up. GCX_PREEMP(); @@ -463,6 +473,18 @@ void EventPipe::Disable() } } +EventPipeSession* EventPipe::GetSession(EventPipeSessionID id) +{ + LIMITED_METHOD_CONTRACT; + + EventPipeSession *pSession = NULL; + if((EventPipeSessionID)s_pSession == id) + { + pSession = s_pSession; + } + return pSession; +} + bool EventPipe::Enabled() { LIMITED_METHOD_CONTRACT; @@ -1026,7 +1048,7 @@ EventPipeEventInstance* EventPipe::GetNextEvent() return pInstance; } -void QCALLTYPE EventPipeInternal::Enable( +UINT64 QCALLTYPE EventPipeInternal::Enable( __in_z LPCWSTR outputFile, UINT32 circularBufferSizeInMB, INT64 profilerSamplingRateInNanoseconds, @@ -1035,21 +1057,48 @@ void QCALLTYPE EventPipeInternal::Enable( { QCALL_CONTRACT; + UINT64 sessionID = 0; + BEGIN_QCALL; SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds); - EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders); + sessionID = EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders); END_QCALL; + + return sessionID; } -void QCALLTYPE EventPipeInternal::Disable() +void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID) { QCALL_CONTRACT; BEGIN_QCALL; - EventPipe::Disable(); + EventPipe::Disable(sessionID); END_QCALL; } +bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo) +{ + QCALL_CONTRACT; + + bool retVal = false; + BEGIN_QCALL; + + if(pSessionInfo != NULL) + { + EventPipeSession *pSession = EventPipe::GetSession(sessionID); + if(pSession != NULL) + { + pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime(); + pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart; + QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency); + retVal = true; + } + } + + END_QCALL; + return retVal; +} + INT_PTR QCALLTYPE EventPipeInternal::CreateProvider( __in_z LPCWSTR providerName, EventPipeCallback pCallbackFunc) @@ -1235,6 +1284,10 @@ bool QCALLTYPE EventPipeInternal::GetNextEvent( { pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider(); pInstance->EventID = pNextInstance->GetEvent()->GetEventID(); + pInstance->ThreadID = pNextInstance->GetThreadId(); + pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart; + pInstance->ActivityId = *pNextInstance->GetActivityId(); + pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId(); pInstance->Payload = pNextInstance->GetData(); pInstance->PayloadLength = pNextInstance->GetDataLength(); } diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index 1904d45d6c..d5273c4974 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -215,6 +215,8 @@ public: } }; +typedef UINT64 EventPipeSessionID; + class EventPipe { // Declare friends. @@ -236,14 +238,17 @@ class EventPipe static void EnableOnStartup(); // Enable tracing via the event pipe. - static void Enable( + static EventPipeSessionID Enable( LPCWSTR strOutputPath, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, int numProviders); // Disable tracing via the event pipe. - static void Disable(); + static void Disable(EventPipeSessionID id); + + // Get the session for the specified session ID. + static EventPipeSession* GetSession(EventPipeSessionID id); // Specifies whether or not the event pipe is enabled. static bool Enabled(); @@ -288,7 +293,7 @@ class EventPipe private: // Enable the specified EventPipe session. - static void Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); + static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); // Get the EnableOnStartup configuration from environment. static void GetConfigurationFromEnvironment(SString &outputPath, EventPipeSession *pSession); @@ -385,20 +390,34 @@ private: public: void *ProviderID; unsigned int EventID; + unsigned int ThreadID; + LARGE_INTEGER TimeStamp; + GUID ActivityId; + GUID RelatedActivityId; const BYTE *Payload; unsigned int PayloadLength; }; + struct EventPipeSessionInfo + { + public: + FILETIME StartTimeAsUTCFileTime; + LARGE_INTEGER StartTimeStamp; + LARGE_INTEGER TimeStampFrequency; + }; + public: - static void QCALLTYPE Enable( + static UINT64 QCALLTYPE Enable( __in_z LPCWSTR outputFile, UINT32 circularBufferSizeInMB, INT64 profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration *pProviders, INT32 numProviders); - static void QCALLTYPE Disable(); + static void QCALLTYPE Disable(UINT64 sessionID); + + static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo); static INT_PTR QCALLTYPE CreateProvider( __in_z LPCWSTR providerName, diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp index 5f6e415662..b10caf2f5b 100644 --- a/src/vm/eventpipesession.cpp +++ b/src/vm/eventpipesession.cpp @@ -29,6 +29,8 @@ EventPipeSession::EventPipeSession( m_pProviderList = new EventPipeSessionProviderList( pProviders, numProviders); + GetSystemTimeAsFileTime(&m_sessionStartTime); + QueryPerformanceCounter(&m_sessionStartTimeStamp); } EventPipeSession::~EventPipeSession() diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h index c8c2e1ff1b..5518e76097 100644 --- a/src/vm/eventpipesession.h +++ b/src/vm/eventpipesession.h @@ -34,6 +34,12 @@ private: // This determines behavior within the system (e.g. policies around which events to drop, etc.) EventPipeSessionType m_sessionType; + // Start date and time in UTC. + FILETIME m_sessionStartTime; + + // Start timestamp. + LARGE_INTEGER m_sessionStartTimeStamp; + public: // TODO: This needs to be exposed via EventPipe::CreateSession() and EventPipe::DeleteSession() to avoid memory ownership issues. @@ -76,6 +82,20 @@ public: m_rundownEnabled = value; } + // Get the session start time in UTC. + FILETIME GetStartTime() const + { + LIMITED_METHOD_CONTRACT; + return m_sessionStartTime; + } + + // Get the session start timestamp. + LARGE_INTEGER GetStartTimeStamp() const + { + LIMITED_METHOD_CONTRACT; + return m_sessionStartTimeStamp; + } + // Enable all events. // This is used for testing and is controlled via COMPLUS_EnableEventPipe. void EnableAllEvents(); |