Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Expose OSThreadId and TimeStamp on EventWrittenEventArgs #19002

Merged
merged 13 commits into from
Aug 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -1913,13 +1913,25 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object
// 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);
}
}
}
Expand Down Expand Up @@ -2021,14 +2033,24 @@ private unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* chi
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)
Expand Down Expand Up @@ -4603,16 +4625,47 @@ public EventLevel Level
}
}

#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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ public ThreadPriority Priority
[MethodImpl(MethodImplOptions.InternalCall)]
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed partial class RuntimeEventSource : EventSource
/// <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)
Expand All @@ -26,7 +26,13 @@ internal unsafe void ProcessEvent(uint eventID, ReadOnlySpan<Byte> payload)

// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,22 @@ internal struct EventPipeEventInstanceData
{
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
{
Expand Down Expand Up @@ -128,6 +140,8 @@ internal void SetProfilerSamplingRate(TimeSpan minTimeBetweenSamples)

internal static class EventPipe
{
private static UInt64 s_sessionID = 0;

internal static void Enable(EventPipeConfiguration configuration)
{
if(configuration == null)
Expand All @@ -142,7 +156,7 @@ internal static void Enable(EventPipeConfiguration configuration)

EventPipeProviderConfiguration[] providers = configuration.Providers;

EventPipeInternal.Enable(
s_sessionID = EventPipeInternal.Enable(
configuration.OutputFile,
configuration.CircularBufferSizeInMB,
configuration.ProfilerSamplingRateInNanoseconds,
Expand All @@ -152,7 +166,7 @@ internal static void Enable(EventPipeConfiguration configuration)

internal static void Disable()
{
EventPipeInternal.Disable();
EventPipeInternal.Disable(s_sessionID);
}
}

Expand All @@ -162,10 +176,10 @@ internal static class EventPipeInternal
// 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.
Expand All @@ -191,6 +205,13 @@ internal static class EventPipeInternal
[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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ internal EventListenerSubscription(EventKeywords matchAnyKeywords, EventLevel le

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();
Expand Down Expand Up @@ -73,21 +78,23 @@ internal void RemoveEventListener(EventListener listener)

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)
{
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;

Expand All @@ -97,12 +104,28 @@ private void CommitDispatchConfiguration()
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();
Expand Down Expand Up @@ -146,7 +169,8 @@ private unsafe void DispatchEventsToEventListeners()
{
// 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);
}
}

Expand All @@ -157,6 +181,26 @@ private unsafe void DispatchEventsToEventListeners()
}
}
}

/// <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
}
6 changes: 6 additions & 0 deletions src/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions src/pal/src/thread/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}


/*++
Expand Down
Loading