diff --git a/build.cmd b/build.cmd
index be6b2a7fde34..f9b15e42726c 100644
--- a/build.cmd
+++ b/build.cmd
@@ -359,11 +359,12 @@ REM Find python and set it to the variable PYTHON
echo import sys; sys.stdout.write(sys.executable) | (py -3 || py -2 || python3 || python2 || python) > %TEMP%\pythonlocation.txt 2> NUL
set /p PYTHON=<%TEMP%\pythonlocation.txt
+if NOT DEFINED PYTHON (
+ echo %__MsgPrefix%Error: Could not find a python installation
+ exit /b 1
+)
+
if /i "%__BuildNative%"=="1" (
- if NOT DEFINED PYTHON (
- echo %__MsgPrefix%Error: Could not find a python installation
- exit /b 1
- )
echo %__MsgPrefix%Laying out dynamically generated files consumed by the native build system
echo %__MsgPrefix%Laying out dynamically generated Event test files and etmdummy stub functions
@@ -376,6 +377,12 @@ if /i "%__BuildNative%"=="1" (
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEtwProvider.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate %__IntermediatesIncDir% --exc %__SourceDir%\vm\ClrEtwAllMeta.lst || exit /b 1
)
+if /i "%__BuildCoreLib%"=="1" (
+
+ echo %__MsgPrefix%Laying out dynamically generated EventSource classes
+ "!PYTHON!" -B -Wall %__SourceDir%\scripts\genRuntimeEventSources.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate %__IntermediatesEventingDir% || exit /b 1
+)
+
if /i "%__DoCrossArchBuild%"=="1" (
if NOT DEFINED PYTHON (
echo %__MsgPrefix%Error: Could not find a python installation
@@ -392,6 +399,9 @@ if /i "%__DoCrossArchBuild%"=="1" (
echo %__MsgPrefix%Laying out dynamically generated EventPipe Implementation
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate !__CrossCompIntermediatesEventingDir!\eventpipe --nonextern || exit /b 1
+ echo %__MsgPrefix%Laying out dynamically generated EventSource classes
+ "!PYTHON!" -B -Wall %__SourceDir%\scripts\genRuntimeEventSources.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate !__CrossCompIntermediatesEventingDir! || exit /b 1
+
echo %__MsgPrefix%Laying out ETW event logging interface
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEtwProvider.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate !__CrossCompIntermediatesIncDir! --exc %__SourceDir%\vm\ClrEtwAllMeta.lst || exit /b 1
)
diff --git a/build.sh b/build.sh
index 701c6315280e..7b433054f6f4 100755
--- a/build.sh
+++ b/build.sh
@@ -220,6 +220,9 @@ generate_event_logging_sources()
echo "Laying out dynamically generated EventPipe Implementation"
$PYTHON -B $__PythonWarningFlags "$__ProjectRoot/src/scripts/genEventPipe.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__OutputEventingDir/eventpipe"
+ echo "Laying out dynamically generated EventSource classes"
+ $PYTHON -B $__PythonWarningFlags "$__ProjectRoot/src/scripts/genRuntimeEventSources.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__OutputEventingDir"
+
# determine the logging system
case $__BuildOS in
Linux|FreeBSD)
@@ -242,7 +245,7 @@ generate_event_logging_sources()
generate_event_logging()
{
# Event Logging Infrastructure
- if [[ $__SkipCoreCLR == 0 || $__ConfigureOnly == 1 ]]; then
+ if [[ $__SkipCoreCLR == 0 || $__SkipMSCorLib == 0 || $__ConfigureOnly == 1 ]]; then
generate_event_logging_sources "$__IntermediatesDir" "the native build system"
fi
diff --git a/clr.coreclr.props b/clr.coreclr.props
index 029ddca4f729..8b546c66b957 100644
--- a/clr.coreclr.props
+++ b/clr.coreclr.props
@@ -4,7 +4,7 @@
true
true
true
-
+ true
true
@@ -17,7 +17,6 @@
true
true
- true
diff --git a/src/mscorlib/ILLinkTrim.xml b/src/mscorlib/ILLinkTrim.xml
index 8c1071d54687..e4e83e37166a 100644
--- a/src/mscorlib/ILLinkTrim.xml
+++ b/src/mscorlib/ILLinkTrim.xml
@@ -35,5 +35,7 @@
+
+
diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj
index 3a0c6b62659b..fc76ffa81f61 100644
--- a/src/mscorlib/System.Private.CoreLib.csproj
+++ b/src/mscorlib/System.Private.CoreLib.csproj
@@ -523,9 +523,13 @@
+
+
+
+
@@ -670,4 +674,4 @@
-
\ No newline at end of file
+
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
index c1e4298da884..6262e3df4633 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
@@ -30,6 +30,13 @@ namespace Microsoft.Diagnostics.Tracing
namespace System.Diagnostics.Tracing
#endif
{
+ internal enum EventProviderType
+ {
+ None = 0,
+ ETW,
+ EventPipe
+ };
+
// New in CLR4.0
internal enum ControllerCommand
{
@@ -120,15 +127,28 @@ public enum WriteEventErrorCode : int
// it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise
// you may get native callbacks during shutdown when we have destroyed the delegate.
// EventSource has special logic to do this, no one else should be calling EventProvider.
- internal EventProvider()
+ internal EventProvider(EventProviderType providerType)
{
+ switch (providerType)
+ {
+ case EventProviderType.ETW:
#if PLATFORM_WINDOWS
- m_eventProvider = new EtwEventProvider();
-#elif FEATURE_PERFTRACING
- m_eventProvider = new EventPipeEventProvider();
+ m_eventProvider = new EtwEventProvider();
#else
- m_eventProvider = new NoOpEventProvider();
+ m_eventProvider = new NoOpEventProvider();
#endif
+ break;
+ case EventProviderType.EventPipe:
+#if FEATURE_PERFTRACING
+ m_eventProvider = new EventPipeEventProvider();
+#else
+ m_eventProvider = new NoOpEventProvider();
+#endif
+ break;
+ default:
+ m_eventProvider = new NoOpEventProvider();
+ break;
+ };
}
///
@@ -475,7 +495,7 @@ private unsafe void GetSessionInfo(SessionInfoCallback action, ref ListNextOffset];
}
-#else
+#else
#if !ES_BUILD_PCL && PLATFORM_WINDOWS // TODO command arguments don't work on PCL builds...
// This code is only used in the Nuget Package Version of EventSource. because
// the code above is using APIs baned from UWP apps.
@@ -1276,8 +1296,7 @@ unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, I
}
}
-#elif !FEATURE_PERFTRACING
-
+#endif
internal sealed class NoOpEventProvider : IEventProvider
{
unsafe uint IEventProvider.EventRegister(
@@ -1314,10 +1333,8 @@ int IEventProvider.EventActivityIdControl(UnsafeNativeMethods.ManifestEtw.Activi
// Define an EventPipeEvent handle.
unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, Int64 keywords, uint eventVersion, uint level, byte *pMetadata, uint metadataLength)
{
- throw new System.NotSupportedException();
+ return IntPtr.Zero;
}
}
-
-#endif
}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
index 1836a1f27d17..b4db4c0d23cd 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -434,7 +434,7 @@ public static void SendCommand(EventSource eventSource, EventCommand command, ID
throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command));
}
- eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
+ eventSource.SendCommand(null, EventProviderType.ETW, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
}
#if !ES_BUILD_STANDALONE
@@ -614,7 +614,7 @@ private unsafe void DefineEventPipeEvents()
}
Debug.Assert(m_eventData != null);
- Debug.Assert(m_provider != null);
+ Debug.Assert(m_eventPipeProvider != null);
int cnt = m_eventData.Length;
for (int i = 0; i < cnt; i++)
{
@@ -632,7 +632,7 @@ private unsafe void DefineEventPipeEvents()
fixed (byte *pMetadata = metadata)
{
- IntPtr eventHandle = m_provider.m_eventProvider.DefineEventHandle(
+ IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(
eventID,
eventName,
keywords,
@@ -1165,12 +1165,16 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel
}
#if FEATURE_MANAGED_ETW
- if (m_eventData[eventId].EnabledForETW)
+ if (m_eventData[eventId].EnabledForETW || m_eventData[eventId].EnabledForEventPipe)
{
if (!SelfDescribingEvents)
{
- if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
ThrowEventSourceException(m_eventData[eventId].Name);
+#if FEATURE_PERFTRACING
+ if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+#endif // FEATURE_PERFTRACING
}
else
{
@@ -1261,7 +1265,6 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
#if FEATURE_MANAGED_ETW
-#if !FEATURE_PERFTRACING
// Send the manifest one more time to ensure circular buffers have a chance to get to this information
// even in scenarios with a high volume of ETW events.
if (m_eventSourceEnabled)
@@ -1274,11 +1277,17 @@ protected virtual void Dispose(bool disposing)
{ } // If it fails, simply give up.
m_eventSourceEnabled = false;
}
+ if (m_etwProvider != null)
+ {
+ m_etwProvider.Dispose();
+ m_etwProvider = null;
+ }
#endif
- if (m_provider != null)
+#if FEATURE_PERFTRACING
+ if (m_eventPipeProvider != null)
{
- m_provider.Dispose();
- m_provider = null;
+ m_eventPipeProvider.Dispose();
+ m_eventPipeProvider = null;
}
#endif
}
@@ -1306,16 +1315,27 @@ private unsafe void WriteEventRaw(
IntPtr data)
{
#if FEATURE_MANAGED_ETW
- if (m_provider == null)
+ if (m_etwProvider == null)
{
ThrowEventSourceException(eventName);
}
else
{
- if (!m_provider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
+ if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
ThrowEventSourceException(eventName);
}
#endif // FEATURE_MANAGED_ETW
+#if FEATURE_PERFTRACING
+ if (m_eventPipeProvider == null)
+ {
+ ThrowEventSourceException(eventName);
+ }
+ else
+ {
+ if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
+ ThrowEventSourceException(eventName);
+ }
+#endif // FEATURE_PERFTRACING
}
// FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
@@ -1364,14 +1384,20 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
//Enable Implicit Activity tracker
m_activityTracker = ActivityTracker.Instance;
-#if FEATURE_MANAGED_ETW
+#if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
// Create and register our provider traits. We do this early because it is needed to log errors
// In the self-describing event case.
this.InitializeProviderMetadata();
-
+#endif
+#if FEATURE_MANAGED_ETW
// Register the provider with ETW
- var provider = new OverideEventProvider(this);
- provider.Register(this);
+ var etwProvider = new OverideEventProvider(this, EventProviderType.ETW);
+ etwProvider.Register(this);
+#endif
+#if FEATURE_PERFTRACING
+ // Register the provider with EventPipe
+ var eventPipeProvider = new OverideEventProvider(this, EventProviderType.EventPipe);
+ eventPipeProvider.Register(this);
#endif
// Add the eventSource to the global (weak) list.
// This also sets m_id, which is the index in the list.
@@ -1380,7 +1406,7 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
#if FEATURE_MANAGED_ETW
// OK if we get this far without an exception, then we can at least write out error messages.
// Set m_provider, which allows this.
- m_provider = provider;
+ m_etwProvider = etwProvider;
#if PLATFORM_WINDOWS
#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
@@ -1394,7 +1420,7 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
- setInformationResult = m_provider.SetInformation(
+ setInformationResult = m_etwProvider.SetInformation(
UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
providerMetadata,
(uint)this.providerMetadata.Length);
@@ -1403,6 +1429,10 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str
}
#endif // PLATFORM_WINDOWS
#endif // FEATURE_MANAGED_ETW
+
+#if FEATURE_PERFTRACING
+ m_eventPipeProvider = eventPipeProvider;
+#endif
Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
// We are logically completely initialized at this point.
m_completelyInited = true;
@@ -1835,12 +1865,16 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object
}
#if FEATURE_MANAGED_ETW
- if (m_eventData[eventId].EnabledForETW)
+ if (m_eventData[eventId].EnabledForETW || m_eventData[eventId].EnabledForEventPipe)
{
if (!SelfDescribingEvents)
{
- if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
+ if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+#if FEATURE_PERFTRACING
+ if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
ThrowEventSourceException(m_eventData[eventId].Name);
+#endif // FEATURE_PERFTRACING
}
else
{
@@ -1865,19 +1899,31 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object
}
}
#endif // FEATURE_MANAGED_ETW
- if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
+ if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
{
#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
// 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);
}
}
}
@@ -1979,14 +2025,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.
- private 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)
@@ -2030,8 +2086,8 @@ private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, E
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
{
-#if FEATURE_MANAGED_ETW && !FEATURE_PERFTRACING
- if (m_provider != null)
+#if FEATURE_MANAGED_ETW
+ if (m_etwProvider != null)
{
string eventName = "EventSourceMessage";
if (SelfDescribingEvents)
@@ -2066,7 +2122,7 @@ private unsafe void WriteEventString(EventLevel level, long keywords, string msg
data.Ptr = (ulong)msgStringPtr;
data.Size = (uint)(2 * (msgString.Length + 1));
data.Reserved = 0;
- m_provider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
+ m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
}
}
}
@@ -2257,19 +2313,22 @@ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string even
///
private class OverideEventProvider : EventProvider
{
- public OverideEventProvider(EventSource eventSource)
+ public OverideEventProvider(EventSource eventSource, EventProviderType providerType)
+ : base(providerType)
{
this.m_eventSource = eventSource;
+ this.m_eventProviderType = providerType;
}
protected override void OnControllerCommand(ControllerCommand command, IDictionary arguments,
int perEventSourceSessionId, int etwSessionId)
{
// We use null to represent the ETW EventListener.
EventListener listener = null;
- m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
+ m_eventSource.SendCommand(listener, m_eventProviderType, perEventSourceSessionId, etwSessionId,
(EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
}
private EventSource m_eventSource;
+ private EventProviderType m_eventProviderType;
}
#endif
@@ -2298,7 +2357,8 @@ partial struct EventMetadata
public IntPtr EventHandle; // EventPipeEvent handle.
public EventTags Tags;
public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
- public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
+ public bool EnabledForETW; // is this event on for ETW?
+ public bool EnabledForEventPipe; // is this event on for EventPipe?
public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
#pragma warning disable 0649
@@ -2342,12 +2402,12 @@ partial struct EventMetadata
// * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
//
// dispatcher == null has special meaning. It is the 'ETW' dispatcher.
- internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
+ internal void SendCommand(EventListener listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId,
EventCommand command, bool enable,
EventLevel level, EventKeywords matchAnyKeyword,
IDictionary commandArguments)
{
- var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
+ var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, eventProviderType, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
lock (EventListener.EventListenersLock)
{
if (m_completelyInited)
@@ -2387,9 +2447,13 @@ internal void DoCommand(EventCommandEventArgs commandArgs)
Debug.Assert(m_completelyInited);
#if FEATURE_MANAGED_ETW
- if (m_provider == null) // If we failed to construct
+ if (m_etwProvider == null) // If we failed to construct
return;
#endif // FEATURE_MANAGED_ETW
+#if FEATURE_PERFTRACING
+ if (m_eventPipeProvider == null)
+ return;
+#endif
m_outOfBandMessageCount = 0;
bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
@@ -2412,7 +2476,7 @@ internal void DoCommand(EventCommandEventArgs commandArgs)
{
// Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
for (int i = 0; i < m_eventData.Length; i++)
- EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
+ EnableEventForDispatcher(commandArgs.dispatcher, commandArgs.eventProviderType, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
if (commandArgs.enable)
{
@@ -2462,13 +2526,11 @@ internal void DoCommand(EventCommandEventArgs commandArgs)
{
// eventSourceDispatcher == null means this is the ETW manifest
-#if !FEATURE_PERFTRACING
// Note that we unconditionally send the manifest whenever we are enabled, even if
// we were already enabled. This is because there may be multiple sessions active
// and we can't know that all the sessions have seen the manifest.
if (!SelfDescribingEvents)
SendManifest(m_rawManifest);
-#endif
}
// Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
@@ -2550,15 +2612,19 @@ internal void DoCommand(EventCommandEventArgs commandArgs)
/// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
/// range return false, otherwise true.
///
- internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
+ internal bool EnableEventForDispatcher(EventDispatcher dispatcher, EventProviderType eventProviderType, int eventId, bool value)
{
if (dispatcher == null)
{
if (eventId >= m_eventData.Length)
return false;
#if FEATURE_MANAGED_ETW
- if (m_provider != null)
+ if (m_etwProvider != null && eventProviderType == EventProviderType.ETW)
m_eventData[eventId].EnabledForETW = value;
+#endif
+#if FEATURE_PERFTRACING
+ if (m_eventPipeProvider != null && eventProviderType == EventProviderType.EventPipe)
+ m_eventData[eventId].EnabledForEventPipe = value;
#endif
}
else
@@ -2578,7 +2644,7 @@ internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId,
private bool AnyEventEnabled()
{
for (int i = 0; i < m_eventData.Length; i++)
- if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
+ if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForEventPipe || m_eventData[i].EnabledForAnyListener)
return true;
return false;
}
@@ -2698,9 +2764,9 @@ private unsafe bool SendManifest(byte[] rawManifest)
while (dataLeft > 0)
{
dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
- if (m_provider != null)
+ if (m_etwProvider != null)
{
- if (!m_provider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
+ if (!m_etwProvider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
{
// Turns out that if users set the BufferSize to something less than 64K then WriteEvent
// can fail. If we get this failure on the first chunk try again with something smaller
@@ -3639,7 +3705,10 @@ private bool SelfDescribingEvents
// Dispatching state
internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
#if FEATURE_MANAGED_ETW
- private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
+ private volatile OverideEventProvider m_etwProvider; // This hooks up ETW commands to our 'OnEventCommand' callback
+#endif
+#if FEATURE_PERFTRACING
+ private volatile OverideEventProvider m_eventPipeProvider;
#endif
private bool m_completelyInited; // The EventSource constructor has returned without exception.
private Exception m_constructionException; // If there was an exception construction, this is it
@@ -3883,7 +3952,14 @@ public void EnableEvents(EventSource eventSource, EventLevel level, EventKeyword
throw new ArgumentNullException(nameof(eventSource));
}
- eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
+ eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
+
+#if FEATURE_PERFTRACING
+ if (eventSource.GetType() == typeof(RuntimeEventSource))
+ {
+ EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, true, level, matchAnyKeyword);
+ }
+#endif // FEATURE_PERFTRACING
}
///
/// Disables all events coming from eventSource identified by 'eventSource'.
@@ -3897,7 +3973,14 @@ public void DisableEvents(EventSource eventSource)
throw new ArgumentNullException(nameof(eventSource));
}
- eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
+ eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
+
+#if FEATURE_PERFTRACING
+ if (eventSource.GetType() == typeof(RuntimeEventSource))
+ {
+ EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None);
+ }
+#endif // FEATURE_PERFTRACING
}
///
@@ -4069,6 +4152,11 @@ private static void RemoveReferencesToListenerInEventSources(EventListener liste
}
}
}
+
+#if FEATURE_PERFTRACING
+ // Remove the listener from the EventPipe dispatcher.
+ EventPipeEventDispatcher.Instance.RemoveEventListener(listenerToRemove);
+#endif // FEATURE_PERFTRACING
}
///
@@ -4242,7 +4330,7 @@ public bool EnableEvent(int eventId)
{
if (Command != EventCommand.Enable && Command != EventCommand.Disable)
throw new InvalidOperationException();
- return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
+ return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, true);
}
///
@@ -4254,18 +4342,19 @@ public bool DisableEvent(int eventId)
{
if (Command != EventCommand.Enable && Command != EventCommand.Disable)
throw new InvalidOperationException();
- return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
+ return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, false);
}
#region private
internal EventCommandEventArgs(EventCommand command, IDictionary arguments, EventSource eventSource,
- EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
+ EventListener listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
{
this.Command = command;
this.Arguments = arguments;
this.eventSource = eventSource;
this.listener = listener;
+ this.eventProviderType = eventProviderType;
this.perEventSourceSessionId = perEventSourceSessionId;
this.etwSessionId = etwSessionId;
this.enable = enable;
@@ -4275,6 +4364,7 @@ internal EventCommandEventArgs(EventCommand command, IDictionary
internal EventSource eventSource;
internal EventDispatcher dispatcher;
+ internal EventProviderType eventProviderType;
// These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
internal EventListener listener;
@@ -4518,16 +4608,47 @@ public EventLevel Level
}
}
-#region private
+ ///
+ /// Gets the identifier for the OS thread that wrote the event.
+ ///
+ public long OSThreadId
+ {
+ get
+ {
+ if (!m_osThreadId.HasValue)
+ {
+ m_osThreadId = (long)RuntimeThread.CurrentOSThreadId;
+ }
+
+ return m_osThreadId.Value;
+ }
+ internal set
+ {
+ m_osThreadId = value;
+ }
+ }
+
+ ///
+ /// Gets a UTC DateTime that specifies when the event was written.
+ ///
+ 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 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/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
index ccdc8bf7c4d4..474d21e82754 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -437,7 +437,7 @@ private unsafe void WriteMultiMergeInner(
EventDescriptor descriptor = new EventDescriptor(identity, level, opcode, (long)keywords);
#if FEATURE_PERFTRACING
- IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, m_eventHandleMap, descriptor, eventTypes);
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleMap, descriptor, eventTypes);
Debug.Assert(eventHandle != IntPtr.Zero);
#else
IntPtr eventHandle = IntPtr.Zero;
@@ -552,7 +552,7 @@ internal unsafe void WriteMultiMerge(
}
#if FEATURE_PERFTRACING
- IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, m_eventHandleMap, descriptor, eventTypes);
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleMap, descriptor, eventTypes);
Debug.Assert(eventHandle != IntPtr.Zero);
#else
IntPtr eventHandle = IntPtr.Zero;
@@ -621,7 +621,7 @@ private unsafe void WriteImpl(
}
#if FEATURE_PERFTRACING
- IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, m_eventHandleMap, descriptor, eventTypes);
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_eventPipeProvider, m_eventHandleMap, descriptor, eventTypes);
Debug.Assert(eventHandle != IntPtr.Zero);
#else
IntPtr eventHandle = IntPtr.Zero;
diff --git a/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs b/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
index 4678be6c8ba0..a708b305e11a 100644
--- a/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
+++ b/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
@@ -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.
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs b/src/mscorlib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs
new file mode 100644
index 000000000000..42cf77959ba4
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/DotNETRuntimeEventSource.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ ///
+ /// RuntimeEventSource is an EventSource that represents the ETW/EventPipe events emitted by the native runtime.
+ /// Most of RuntimeEventSource is auto-generated by scripts/genRuntimeEventSources.py based on the contents of the Microsoft-Windows-DotNETRuntime provider.
+ ///
+ internal sealed partial class RuntimeEventSource : EventSource
+ {
+ ///
+ /// Dispatch a single event with the specified event ID and payload.
+ ///
+ /// The eventID corresponding to the event as defined in the auto-generated portion of the RuntimeEventSource class.
+ /// A span pointing to the data payload for the event.
+ [NonEvent]
+ internal unsafe void ProcessEvent(uint eventID, uint osThreadID, DateTime timeStamp, Guid activityId, Guid childActivityId, ReadOnlySpan payload)
+ {
+ // Make sure the eventID is valid.
+ if (eventID >= m_eventData.Length)
+ {
+ return;
+ }
+
+ // Decode the payload.
+ object[] decodedPayloadFields = EventPipePayloadDecoder.DecodePayload(ref m_eventData[eventID], payload);
+ WriteToAllListeners(
+ eventId: (int)eventID,
+ osThreadId: &osThreadID,
+ timeStamp: &timeStamp,
+ activityID: &activityId,
+ childActivityID: &childActivityId,
+ args: decodedPayloadFields);
+ }
+ }
+#endif // FEATURE_PERFTRACING
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
index 58091be27ff9..94ace3cca59d 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
@@ -7,8 +7,31 @@
using System.Security;
using Microsoft.Win32;
+#if FEATURE_PERFTRACING
+
namespace System.Diagnostics.Tracing
{
+ [StructLayout(LayoutKind.Sequential)]
+ 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
{
@@ -117,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)
@@ -131,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,
@@ -141,7 +166,7 @@ internal static void Enable(EventPipeConfiguration configuration)
internal static void Disable()
{
- EventPipeInternal.Disable();
+ EventPipeInternal.Disable(s_sessionID);
}
}
@@ -151,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.
@@ -165,6 +190,9 @@ internal static class EventPipeInternal
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, long keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength);
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern IntPtr GetProvider(string providerName);
+
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern void DeleteProvider(IntPtr provHandle);
@@ -176,5 +204,17 @@ 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);
}
}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs
new file mode 100644
index 000000000000..f7afd973c135
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs
@@ -0,0 +1,206 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ internal sealed class EventPipeEventDispatcher
+ {
+ internal sealed class EventListenerSubscription
+ {
+ internal EventKeywords MatchAnyKeywords { get; private set; }
+ internal EventLevel Level { get; private set; }
+
+ internal EventListenerSubscription(EventKeywords matchAnyKeywords, EventLevel level)
+ {
+ MatchAnyKeywords = matchAnyKeywords;
+ Level = level;
+ }
+ }
+
+ internal static readonly EventPipeEventDispatcher Instance = new EventPipeEventDispatcher();
+
+ 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();
+ private Dictionary m_subscriptions = new Dictionary();
+
+ 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(RuntimeEventSource.EventSourceName);
+ }
+
+ internal void SendCommand(EventListener eventListener, EventCommand command, bool enable, EventLevel level, EventKeywords matchAnyKeywords)
+ {
+ if (command == EventCommand.Update && enable)
+ {
+ lock (m_dispatchControlLock)
+ {
+ // Add the new subscription. This will overwrite an existing subscription for the listener if one exists.
+ m_subscriptions[eventListener] = new EventListenerSubscription(matchAnyKeywords, level);
+
+ // Commit the configuration change.
+ CommitDispatchConfiguration();
+ }
+ }
+ else if (command == EventCommand.Update && !enable)
+ {
+ RemoveEventListener(eventListener);
+ }
+ }
+
+ internal void RemoveEventListener(EventListener listener)
+ {
+ lock (m_dispatchControlLock)
+ {
+ // Remove the event listener from the list of subscribers.
+ if (m_subscriptions.ContainsKey(listener))
+ {
+ m_subscriptions.Remove(listener);
+ }
+
+ // Commit the configuration change.
+ CommitDispatchConfiguration();
+ }
+ }
+
+ 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(m_sessionID);
+
+ // Check to see if tracing should be enabled.
+ if (m_subscriptions.Count <= 0)
+ {
+ return;
+ }
+
+ // Determine the keywords and level that should be used based on the set of enabled EventListeners.
+ EventKeywords aggregatedKeywords = EventKeywords.None;
+ EventLevel highestLevel = EventLevel.LogAlways;
+
+ foreach (EventListenerSubscription subscription in m_subscriptions.Values)
+ {
+ aggregatedKeywords |= subscription.MatchAnyKeywords;
+ highestLevel = (subscription.Level > highestLevel) ? subscription.Level : highestLevel;
+ }
+
+ // Enable the EventPipe session.
+ EventPipeProviderConfiguration[] providerConfiguration = new EventPipeProviderConfiguration[]
+ {
+ new EventPipeProviderConfiguration(RuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel)
+ };
+
+ 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();
+ }
+
+ private void StartDispatchTask()
+ {
+ Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
+
+ if (m_dispatchTask == null)
+ {
+ m_stopDispatchTask = false;
+ m_dispatchTask = Task.Factory.StartNew(DispatchEventsToEventListeners, TaskCreationOptions.LongRunning);
+ }
+ }
+
+ private void StopDispatchTask()
+ {
+ Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
+
+ if(m_dispatchTask != null)
+ {
+ m_stopDispatchTask = true;
+ m_dispatchTask.Wait();
+ m_dispatchTask = null;
+ }
+ }
+
+ private unsafe void DispatchEventsToEventListeners()
+ {
+ // Struct to fill with the call to GetNextEvent.
+ EventPipeEventInstanceData instanceData;
+
+ while (!m_stopDispatchTask)
+ {
+ // Get the next event.
+ while (!m_stopDispatchTask && EventPipeInternal.GetNextEvent(&instanceData))
+ {
+ // Filter based on provider.
+ if (instanceData.ProviderID == m_RuntimeProviderID)
+ {
+ // Dispatch the event.
+ ReadOnlySpan payload = new ReadOnlySpan((void*)instanceData.Payload, (int)instanceData.PayloadLength);
+ DateTime dateTimeStamp = TimeStampToDateTime(instanceData.TimeStamp);
+ RuntimeEventSource.Log.ProcessEvent(instanceData.EventID, instanceData.ThreadID, dateTimeStamp, instanceData.ActivityId, instanceData.ChildActivityId, payload);
+ }
+ }
+
+ // Wait for more events.
+ if (!m_stopDispatchTask)
+ {
+ Thread.Sleep(10);
+ }
+ }
+ }
+
+ ///
+ /// Converts a QueryPerformanceCounter (QPC) timestamp to a UTC DateTime.
+ ///
+ 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/mscorlib/src/System/Diagnostics/Eventing/EventPipeMetadataGenerator.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeMetadataGenerator.cs
index d18610d922ca..a5cc756ae6e2 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeMetadataGenerator.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeMetadataGenerator.cs
@@ -384,6 +384,13 @@ private static TypeCode GetTypeCodeExtended(Type parameterType)
if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
return GuidTypeCode;
+ // IntPtr and UIntPtr are converted to their non-pointer types.
+ if (parameterType == typeof(IntPtr))
+ return IntPtr.Size == 4 ? TypeCode.Int32 : TypeCode.Int64;
+
+ if (parameterType == typeof(UIntPtr))
+ return UIntPtr.Size == 4 ? TypeCode.UInt32 : TypeCode.UInt64;
+
return Type.GetTypeCode(parameterType);
}
}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipePayloadDecoder.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipePayloadDecoder.cs
new file mode 100644
index 000000000000..48aa5802dd16
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipePayloadDecoder.cs
@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ internal static class EventPipePayloadDecoder
+ {
+ ///
+ /// Given the metadata for an event and an event payload, decode and deserialize the event payload.
+ ///
+ internal static object[] DecodePayload(ref EventSource.EventMetadata metadata, ReadOnlySpan payload)
+ {
+ ParameterInfo[] parameters = metadata.Parameters;
+ object[] decodedFields = new object[parameters.Length];
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ // It is possible that an older version of the event was emitted.
+ // If this happens, the payload might be missing arguments at the end.
+ // We can just leave these unset.
+ if (payload.Length <= 0)
+ {
+ break;
+ }
+
+ Type parameterType = parameters[i].ParameterType;
+ if (parameterType == typeof(IntPtr))
+ {
+ if (IntPtr.Size == 8)
+ {
+ // Payload is automatically updated to point to the next piece of data.
+ decodedFields[i] = (IntPtr)ReadUnalignedUInt64(ref payload);
+ }
+ else if (IntPtr.Size == 4)
+ {
+ decodedFields[i] = (IntPtr)MemoryMarshal.Read(payload);
+ payload = payload.Slice(IntPtr.Size);
+ }
+ else
+ {
+ Debug.Assert(false, "Unsupported pointer size.");
+ }
+ }
+ else if (parameterType == typeof(int))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(int));
+ }
+ else if (parameterType == typeof(uint))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(uint));
+ }
+ else if (parameterType == typeof(long))
+ {
+ // Payload is automatically updated to point to the next piece of data.
+ decodedFields[i] = (long)ReadUnalignedUInt64(ref payload);
+ }
+ else if (parameterType == typeof(ulong))
+ {
+ // Payload is automatically updated to point to the next piece of data.
+ decodedFields[i] = ReadUnalignedUInt64(ref payload);
+ }
+ else if (parameterType == typeof(byte))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(byte));
+ }
+ else if (parameterType == typeof(sbyte))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(sbyte));
+ }
+ else if (parameterType == typeof(short))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(short));
+ }
+ else if (parameterType == typeof(ushort))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(ushort));
+ }
+ else if (parameterType == typeof(float))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(float));
+ }
+ else if (parameterType == typeof(double))
+ {
+ // Payload is automatically updated to point to the next piece of data.
+ Int64 doubleBytes = (Int64)ReadUnalignedUInt64(ref payload);
+ decodedFields[i] = BitConverter.Int64BitsToDouble(doubleBytes);
+ }
+ else if (parameterType == typeof(bool))
+ {
+ // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
+ decodedFields[i] = (MemoryMarshal.Read(payload) == 1);
+ payload = payload.Slice(sizeof(int));
+ }
+ else if (parameterType == typeof(Guid))
+ {
+ // Payload is automatically updated to point to the next piece of data.
+ decodedFields[i] = ReadUnalignedGuid(ref payload);
+ }
+ else if (parameterType == typeof(char))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(char));
+ }
+ else if (parameterType == typeof(string))
+ {
+ ReadOnlySpan charPayload = MemoryMarshal.Cast(payload);
+ int charCount = 0;
+ foreach(char c in charPayload)
+ {
+ if (c == '\0')
+ break;
+ charCount++;
+ }
+ string val = new string(charPayload.ToArray(), 0, charCount);
+ payload = payload.Slice((val.Length + 1) * sizeof(char));
+ decodedFields[i] = val;
+ }
+ else
+ {
+ Debug.Assert(false, "Unsupported type encountered.");
+ }
+ }
+
+ return decodedFields;
+ }
+
+ private static UInt64 ReadUnalignedUInt64(ref ReadOnlySpan payload)
+ {
+ UInt64 val = 0;
+ if (BitConverter.IsLittleEndian)
+ {
+ val |= MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(UInt32));
+ val |= (MemoryMarshal.Read(payload) << sizeof(UInt32));
+ payload = payload.Slice(sizeof(UInt32));
+ }
+ else
+ {
+ val |= (MemoryMarshal.Read(payload) << sizeof(UInt32));
+ payload = payload.Slice(sizeof(UInt32));
+ val |= MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(UInt32));
+ }
+
+ return val;
+ }
+
+ private static Guid ReadUnalignedGuid(ref ReadOnlySpan payload)
+ {
+ const int sizeOfGuid = 16;
+ byte[] guidBytes = new byte[sizeOfGuid];
+ if (BitConverter.IsLittleEndian)
+ {
+ for (int i = sizeOfGuid - 1; i >= 0; i--)
+ {
+ guidBytes[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(byte));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < sizeOfGuid; i++)
+ {
+ guidBytes[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(byte));
+ }
+ }
+
+ return new Guid(guidBytes);
+ }
+ }
+#endif // FEATURE_PERFTRACING
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/XplatEventLogger.cs b/src/mscorlib/src/System/Diagnostics/Eventing/XplatEventLogger.cs
index 31f79f5218e2..69473dc85b16 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/XplatEventLogger.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/XplatEventLogger.cs
@@ -156,6 +156,12 @@ private static void AppendByteArrayAsHexString(StringBuilder builder, byte[] byt
internal protected override void OnEventSourceCreated(EventSource eventSource)
{
+ // Don't enable forwarding of RuntimeEventSource events.`
+ if (eventSource.GetType() == typeof(RuntimeEventSource))
+ {
+ return;
+ }
+
string eventSourceFilter = eventSourceNameFilter.Value;
if (String.IsNullOrEmpty(eventSourceFilter) || (eventSource.Name.IndexOf(eventSourceFilter, StringComparison.OrdinalIgnoreCase) >= 0))
{
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index 60f4a81c66e4..6b8b8ccc5b5e 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -1261,6 +1261,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 bc06d2f7d499..66311edb0366 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/scripts/genRuntimeEventSources.py b/src/scripts/genRuntimeEventSources.py
new file mode 100644
index 000000000000..1bde74ad8be1
--- /dev/null
+++ b/src/scripts/genRuntimeEventSources.py
@@ -0,0 +1,444 @@
+#
+## Licensed to the .NET Foundation under one or more agreements.
+## The .NET Foundation licenses this file to you under the MIT license.
+## See the LICENSE file in the project root for more information.
+#
+
+import os
+import xml.dom.minidom as DOM
+from utilities import open_for_update
+import argparse
+import sys
+
+generatedCodeFileHeader="""// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/**********************************************************************
+
+DO NOT MODIFY. AUTOGENERATED FILE.
+This file is generated by /src/scripts/genRuntimeEventSources.py
+
+**********************************************************************/
+"""
+
+########################################################################
+# START CONFIGURATION
+########################################################################
+manifestsToGenerate = {
+ "Microsoft-Windows-DotNETRuntime" : "DotNETRuntimeEventSource.cs"
+}
+
+providerNameToClassNameMap = {
+ "Microsoft-Windows-DotNETRuntime" : "RuntimeEventSource"
+}
+
+manifestTypeToCSharpTypeMap = {
+ "win:UInt8" : "byte",
+ "win:UInt16" : "UInt16",
+ "win:UInt32" : "UInt32",
+ "win:UInt64" : "UInt64",
+ "win:Int32" : "Int32",
+ "win:Pointer" : "IntPtr",
+ "win:UnicodeString" : "string",
+ "win:Binary" : "byte[]",
+ "win:Double" : "double",
+ "win:Boolean" : "bool",
+ "win:GUID" : "Guid",
+}
+
+overrideEnumBackingTypes = {
+ "Microsoft-Windows-DotNETRuntime" : {
+ "GCSuspendEEReasonMap" : "win:UInt32",
+ "GCRootKindMap" : "win:UInt32"
+ }
+}
+########################################################################
+# END CONFIGURATION
+########################################################################
+
+tabText = ""
+
+def increaseTabLevel():
+ global tabText
+ tabText += " "
+
+def decreaseTabLevel():
+ global tabText
+ tabText = tabText[:-4]
+
+def writeOutput(outputFile, str):
+ outputFile.write(tabText + str)
+
+def getCSharpTypeFromManifestType(manifestType):
+ return manifestTypeToCSharpTypeMap[manifestType]
+
+def generateEvent(eventNode, providerNode, outputFile, stringTable):
+
+ # Write the event attribute.
+ writeOutput(outputFile, "[Event("+ eventNode.getAttribute("value") + ", Version = " + eventNode.getAttribute("version") + ", Level = EventLevel." + eventNode.getAttribute("level")[4:])
+
+ # Not all events have keywords specified, and some have multiple keywords specified.
+ keywords = eventNode.getAttribute("keywords")
+ if keywords:
+ if " " not in keywords:
+ outputFile.write(", Keywords = Keywords." + keywords)
+ else:
+ keywords = keywords.split()
+ outputFile.write(", Keywords = ")
+ for keywordIndex in range(len(keywords)):
+ outputFile.write("Keywords." + keywords[keywordIndex])
+ if keywordIndex < (len(keywords) - 1):
+ outputFile.write(" | ")
+
+ outputFile.write(")]\n")
+
+ # Get the template for the event.
+ templateNode = None
+ templateKey = eventNode.getAttribute("template")
+ if templateKey is not None:
+ for node in providerNode.getElementsByTagName("templates"):
+ templatesNode = node
+ break
+ for node in templatesNode.getElementsByTagName("template"):
+ if node.getAttribute("tid") == templateKey:
+ templateNode = node
+ break
+
+ # Write the beginning of the method signature.
+ writeOutput(outputFile, "private void " + eventNode.getAttribute("symbol") + "(")
+
+ # Write the function signature.
+ argumentCount = 0
+ if templateNode is not None:
+ argumentNodes = templateNode.childNodes
+
+ # Calculate the number of arguments.
+ for argumentNode in argumentNodes:
+ if argumentNode.nodeName == "data":
+ if argumentNode.getAttribute("inType") != "win:Binary" and argumentNode.getAttribute("inType") != "win:AnsiString" and argumentNode.getAttribute("count") == "":
+ argumentCount += 1
+ else:
+ break
+ elif argumentNode.nodeName == "struct":
+ break
+
+ argumentsProcessed = 0
+ for argumentIndex in range(len(argumentNodes)):
+ argumentNode = argumentNodes[argumentIndex]
+ if argumentNode.nodeName == "data":
+ argumentName = argumentNode.getAttribute("name")
+ argumentInType = argumentNode.getAttribute("inType")
+
+ #### Disable enums until they are needed ####
+ # argumentMap = argumentNode.getAttribute("map")
+ # if not argumentMap:
+ # argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
+ # else:
+ # argumentCSharpType = argumentMap[:-3]
+ #### Disable enums until they are needed ####
+
+ argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
+ outputFile.write(argumentCSharpType + " " + argumentName)
+ argumentsProcessed += 1
+ if argumentsProcessed < argumentCount:
+ outputFile.write(", ")
+ if argumentsProcessed == argumentCount:
+ break
+
+ outputFile.write(")\n")
+ writeOutput(outputFile, "{\n")
+
+ # Write the call to WriteEvent.
+ increaseTabLevel()
+ writeOutput(outputFile, "WriteEvent(" + eventNode.getAttribute("value"))
+
+ # Add method parameters.
+ if argumentCount > 0:
+ # A ',' is needed after the event id.
+ outputFile.write(", ")
+
+ # Write the parameter names to the method call.
+ argumentsProcessed = 0
+ argumentNodes = templateNode.getElementsByTagName("data")
+ for argumentIndex in range(argumentCount):
+ argumentNode = argumentNodes[argumentIndex]
+ argumentName = argumentNode.getAttribute("name")
+ outputFile.write(argumentName)
+ if argumentIndex < (argumentCount - 1):
+ outputFile.write(", ")
+
+ outputFile.write(");\n")
+ decreaseTabLevel()
+
+ writeOutput(outputFile, "}\n\n")
+
+
+def generateEvents(providerNode, outputFile, stringTable):
+
+ # Get the events element.
+ for node in providerNode.getElementsByTagName("events"):
+ eventsNode = node
+ break
+
+ # Get the list of event nodes.
+ eventNodes = eventsNode.getElementsByTagName("event")
+
+ # Build a list of events to be emitted. This is where old versions of events are stripped.
+ # key = eventID, value = version
+ eventList = dict()
+ for eventNode in eventNodes:
+ eventID = eventNode.getAttribute("value")
+ eventVersion = eventNode.getAttribute("version")
+ eventList[eventID] = eventVersion
+
+ # Iterate over each event node and process it.
+ # Only emit events for the latest version of the event, otherwise EventSource initialization will fail.
+ for eventNode in eventNodes:
+ eventID = eventNode.getAttribute("value")
+ eventVersion = eventNode.getAttribute("version")
+ if eventID in eventList and eventList[eventID] == eventVersion:
+ generateEvent(eventNode, providerNode, outputFile, stringTable)
+ elif eventID not in eventList:
+ raise ValueError("eventID could not be found in the list of events to emit.", eventID)
+
+def generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
+
+ # Get the maps element.
+ for node in providerNode.getElementsByTagName("maps"):
+ mapsNode = node
+ break
+
+ # Iterate over each map and create an enum out of it.
+ for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
+
+ # Get the backing type of the enum.
+ typeName = enumTypeMap[valueMapNode.getAttribute("name")]
+ if typeName is None:
+ raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
+
+ enumType = getCSharpTypeFromManifestType(typeName)
+ writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
+ writeOutput(outputFile, "{\n")
+ increaseTabLevel()
+ for mapNode in valueMapNode.getElementsByTagName("map"):
+ # Each map value has a message, which we should use as the enum value.
+ messageKey = mapNode.getAttribute("message")[9:-1]
+ writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n")
+ decreaseTabLevel()
+ writeOutput(outputFile, "}\n\n")
+
+def generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
+
+ # Get the maps element.
+ for node in providerNode.getElementsByTagName("maps"):
+ mapsNode = node
+ break
+
+ # Iterate over each map and create an enum out of it.
+ for valueMapNode in mapsNode.getElementsByTagName("bitMap"):
+
+ # Get the backing type of the enum.
+ typeName = enumTypeMap[valueMapNode.getAttribute("name")]
+ if typeName is None:
+ raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
+
+ enumType = getCSharpTypeFromManifestType(typeName)
+ writeOutput(outputFile, "[Flags]\n")
+ writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
+ writeOutput(outputFile, "{\n")
+ increaseTabLevel()
+ for mapNode in valueMapNode.getElementsByTagName("map"):
+ # Each map value has a message, which we should use as the enum value.
+ messageKey = mapNode.getAttribute("message")[9:-1]
+ writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n")
+ decreaseTabLevel()
+ writeOutput(outputFile, "}\n\n")
+
+def generateEnumTypeMap(providerNode):
+
+ providerName = providerNode.getAttribute("name")
+ templatesNodes = providerNode.getElementsByTagName("templates")
+ templatesNode = templatesNodes[0]
+ mapsNodes = providerNode.getElementsByTagName("maps")
+
+ # Keep a list of mapName -> inType.
+ # This map contains the first inType seen for the specified mapName.
+ typeMap = dict()
+
+ # There are a couple of maps that are used by multiple events but have different backing types.
+ # Because only one of the uses will be consumed by EventSource/EventListener we can hack the backing type here
+ # and suppress the warning that we'd otherwise get.
+ overrideTypeMap = dict()
+ if providerName in overrideEnumBackingTypes:
+ overrideTypeMap = overrideEnumBackingTypes[providerName]
+
+ for mapsNode in mapsNodes:
+ for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
+ mapName = valueMapNode.getAttribute("name")
+ dataNodes = templatesNode.getElementsByTagName("data")
+
+ # If we've never seen the map used, save its usage with the inType.
+ # If we have seen the map used, make sure that the inType saved previously matches the current inType.
+ for dataNode in dataNodes:
+ if dataNode.getAttribute("map") == mapName:
+ if mapName in overrideTypeMap:
+ typeMap[mapName] = overrideTypeMap[mapName]
+ elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"):
+ print("WARNING: Map " + mapName + " is used multiple times with different types. This may cause functional bugs in tracing.")
+ elif not mapName in typeMap:
+ typeMap[mapName] = dataNode.getAttribute("inType")
+ for bitMapNode in mapsNode.getElementsByTagName("bitMap"):
+ mapName = bitMapNode.getAttribute("name")
+ dataNodes = templatesNode.getElementsByTagName("data")
+
+ # If we've never seen the map used, save its usage with the inType.
+ # If we have seen the map used, make sure that the inType saved previously matches the current inType.
+ for dataNode in dataNodes:
+ if dataNode.getAttribute("map") == mapName:
+ if mapName in overrideTypeMap:
+ typeMap[mapName] = overrideTypeMap[mapName]
+ elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"):
+ print("Map " + mapName + " is used multiple times with different types.")
+ elif not mapName in typeMap:
+ typeMap[mapName] = dataNode.getAttribute("inType")
+
+ return typeMap
+
+def generateKeywordsClass(providerNode, outputFile):
+
+ # Find the keywords element.
+ for node in providerNode.getElementsByTagName("keywords"):
+ keywordsNode = node
+ break;
+
+ writeOutput(outputFile, "public class Keywords\n")
+ writeOutput(outputFile, "{\n")
+ increaseTabLevel()
+
+ for keywordNode in keywordsNode.getElementsByTagName("keyword"):
+ writeOutput(outputFile, "public const EventKeywords " + keywordNode.getAttribute("name") + " = (EventKeywords)" + keywordNode.getAttribute("mask") + ";\n")
+
+ decreaseTabLevel()
+ writeOutput(outputFile, "}\n\n")
+
+def loadStringTable(manifest):
+
+ # Create the string table dictionary.
+ stringTable = dict()
+
+ # Get the string table element.
+ for node in manifest.getElementsByTagName("stringTable"):
+ stringTableNode = node
+ break
+
+ # Iterate through each string and save it.
+ for stringElem in stringTableNode.getElementsByTagName("string"):
+ stringTable[stringElem.getAttribute("id")] = stringElem.getAttribute("value")
+
+ return stringTable
+
+def generateEventSources(manifestFullPath, intermediatesDirFullPath):
+
+ # Open the manifest for reading.
+ manifest = DOM.parse(manifestFullPath)
+
+ # Load the string table.
+ stringTable = loadStringTable(manifest)
+
+ # Iterate over each provider that we want to generate an EventSource for.
+ for providerName, outputFileName in manifestsToGenerate.items():
+ for node in manifest.getElementsByTagName("provider"):
+ if node.getAttribute("name") == providerName:
+ providerNode = node
+ break
+
+ if providerNode is None:
+ raise ValueError("Unable to find provider node.", providerName)
+
+ # Generate a full path to the output file and open the file for open_for_update.
+ outputFilePath = os.path.join(intermediatesDirFullPath, outputFileName)
+ with open_for_update(outputFilePath) as outputFile:
+
+ # Write the license header.
+ writeOutput(outputFile, generatedCodeFileHeader)
+
+ # Write the class header.
+ header = """
+using System;
+
+namespace System.Diagnostics.Tracing
+{
+"""
+ writeOutput(outputFile, header)
+ increaseTabLevel()
+
+ className = providerNameToClassNameMap[providerName]
+ writeOutput(outputFile, "[EventSource(Name = \"" + providerName + "\")]\n")
+ writeOutput(outputFile, "internal sealed partial class " + className + " : EventSource\n")
+ writeOutput(outputFile, "{\n")
+ increaseTabLevel()
+
+ # Create a static property for the EventSource name so that we don't have to initialize the EventSource to get its name.
+ writeOutput(outputFile, "internal const string EventSourceName = \"" + providerName + "\";\n")
+
+ # Write the static Log property.
+ writeOutput(outputFile, "internal static " + className + " Log = new " + className + "();\n\n")
+
+ # Write the keywords class.
+ generateKeywordsClass(providerNode, outputFile)
+
+ #### Disable enums until they are needed ####
+ # Generate the enum type map.
+ # This determines what the backing type for each enum should be.
+ # enumTypeMap = generateEnumTypeMap(providerNode)
+
+ # Generate enums for value maps.
+ # generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
+
+ # Generate enums for bit maps.
+ # generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
+ #### Disable enums until they are needed ####
+
+ # Generate events.
+ generateEvents(providerNode, outputFile, stringTable)
+
+ # Write the class footer.
+ decreaseTabLevel()
+ writeOutput(outputFile, "}\n")
+ decreaseTabLevel()
+ writeOutput(outputFile, "}")
+
+def main(argv):
+
+ # Parse command line arguments.
+ parser = argparse.ArgumentParser(
+ description="Generates C# EventSource classes that represent the runtime's native event providers.")
+
+ required = parser.add_argument_group('required arguments')
+ required.add_argument('--man', type=str, required=True,
+ help='full path to manifest containig the description of events')
+ required.add_argument('--intermediate', type=str, required=True,
+ help='full path to eventprovider intermediate directory')
+ args, unknown = parser.parse_known_args(argv)
+ if unknown:
+ print('Unknown argument(s): ', ', '.join(unknown))
+ return 1
+
+ manifestFullPath = args.man
+ intermediatesDirFullPath = args.intermediate
+
+ # Ensure the intermediates directory exists.
+ try:
+ os.makedirs(intermediatesDirFullPath)
+ except OSError:
+ if not os.path.isdir(intermediatesDirFullPath):
+ raise
+
+ # Generate event sources.
+ generateEventSources(manifestFullPath, intermediatesDirFullPath)
+ return 0
+
+if __name__ == '__main__':
+ return_code = main(sys.argv[1:])
+ sys.exit(return_code)
diff --git a/src/scripts/scripts.pyproj b/src/scripts/scripts.pyproj
new file mode 100644
index 000000000000..c0b7bef6415e
--- /dev/null
+++ b/src/scripts/scripts.pyproj
@@ -0,0 +1,38 @@
+
+
+
+ Debug
+ 2.0
+ {9d304b80-696f-4873-85ca-cbd2fdb900af}
+
+ genRuntimeEventSources.py
+
+ .
+ .
+ {888888a0-9f3d-457c-b088-3a5042f75d52}
+ Standard Python launcher
+ Global|PythonCore|2.7
+ --man c:\src\coreclr\src\vm\clretwall.man --intermediate c:\work\runtimeeventsource
+ False
+
+
+
+
+ 10.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/vm/ClrEtwAll.man b/src/vm/ClrEtwAll.man
index 3a3568468cb8..1081477bfb63 100644
--- a/src/vm/ClrEtwAll.man
+++ b/src/vm/ClrEtwAll.man
@@ -75,6 +75,8 @@
message="$(string.RuntimePublisher.MonitoringKeywordMessage)" symbol="CLR_MONITORING_KEYWORD" />
+
@@ -1816,7 +1818,7 @@
-
+
@@ -1837,6 +1839,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+ %6
+ %7
+ %8
+ %9
+ %10
+ %11
+ %12
+
+
+
+
@@ -1880,7 +1914,7 @@
-
+
@@ -1900,6 +1934,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+ %6
+ %7
+ %8
+ %9
+ %10
+ %11
+ %12
+
+
+
@@ -2961,10 +3027,10 @@
symbol="MethodJitInliningSucceeded"
message="$(string.RuntimePublisher.MethodJitInliningSucceededEventMessage)"/>
-
-
+
+
+
+
@@ -5052,6 +5130,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -6017,11 +6107,11 @@
task="GarbageCollectionPrivate"
symbol="PrvFinalizeObject" message="$(string.PrivatePublisher.FinalizeObjectEventMessage)"/>
-
+ symbol="CCWRefCountChangeAnsi" message="$(string.PrivatePublisher.CCWRefCountChangeEventMessage)"/>
+
+
@@ -6759,6 +6855,7 @@
+
diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp
index 5c2e1876e1cf..0f9638dacf90 100644
--- a/src/vm/comcallablewrapper.cpp
+++ b/src/vm/comcallablewrapper.cpp
@@ -958,13 +958,26 @@ void SimpleComCallWrapper::BuildRefCountLogMessage(LPCWSTR wszOperation, StackSS
obj = *((_UNCHECKED_OBJECTREF *)(handle));
if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, CCWRefCountChange))
- FireEtwCCWRefCountChange(
- handle,
- (Object *)obj,
- this,
- dwEstimatedRefCount,
- NULL, // domain value is not interesting in CoreCLR
- pszClassName, pszNamespace, 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 772163aff291..b2515a162023 100644
--- a/src/vm/comsynchronizable.cpp
+++ b/src/vm/comsynchronizable.cpp
@@ -794,6 +794,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 87321fd336c2..ddd55cc21f4d 100644
--- a/src/vm/comsynchronizable.h
+++ b/src/vm/comsynchronizable.h
@@ -101,6 +101,7 @@ friend class ThreadBaseObject;
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 677d39b706ed..fce8207091e2 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -674,6 +674,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)
@@ -1188,12 +1189,15 @@ 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)
QCFuncElement("EventActivityIdControl", EventPipeInternal::EventActivityIdControl)
+ QCFuncElement("GetProvider", EventPipeInternal::GetProvider)
QCFuncElement("WriteEvent", EventPipeInternal::WriteEvent)
QCFuncElement("WriteEventData", EventPipeInternal::WriteEventData)
+ QCFuncElement("GetNextEvent", EventPipeInternal::GetNextEvent)
FCFuncEnd()
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index cad3093d4630..42eea423a7a7 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -221,6 +221,7 @@ void EventPipe::EnableOnStartup()
// Create a new session.
EventPipeSession *pSession = new EventPipeSession(
+ EventPipeSessionType::File,
1024 /* 1 GB circular buffer */,
NULL, /* pProviders */
0 /* numProviders */);
@@ -249,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);
@@ -278,7 +279,7 @@ void EventPipe::Shutdown()
#endif
}
-void EventPipe::Enable(
+EventPipeSessionID EventPipe::Enable(
LPCWSTR strOutputPath,
unsigned int circularBufferSizeInMB,
EventPipeProviderConfiguration *pProviders,
@@ -293,13 +294,17 @@ void EventPipe::Enable(
CONTRACTL_END;
// Create a new session.
- EventPipeSession *pSession = s_pConfig->CreateSession(circularBufferSizeInMB, pProviders, static_cast(numProviders));
+ EventPipeSession *pSession = s_pConfig->CreateSession(
+ (strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
+ circularBufferSizeInMB,
+ pProviders,
+ static_cast(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
{
@@ -313,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.
@@ -329,8 +334,13 @@ void EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession)
CrstHolder _crst(GetLock());
// Create the event pipe file.
- SString eventPipeFileOutputPath(strOutputPath);
- s_pFile = new EventPipeFile(eventPipeFileOutputPath);
+ // A NULL output path means that we should not write the results to a file.
+ // This is used in the EventListener streaming case.
+ if (strOutputPath != NULL)
+ {
+ SString eventPipeFileOutputPath(strOutputPath);
+ s_pFile = new EventPipeFile(eventPipeFileOutputPath);
+ }
#ifdef _DEBUG
if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableEventPipe) & 2) == 2)
@@ -355,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
{
@@ -367,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();
@@ -395,39 +415,39 @@ void EventPipe::Disable()
FlushProcessWriteBuffers();
// Write to the file.
- LARGE_INTEGER disableTimeStamp;
- QueryPerformanceCounter(&disableTimeStamp);
- s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
-
- if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
+ if(s_pFile != NULL)
{
- // Before closing the file, do rundown.
- const unsigned int numRundownProviders = 2;
- EventPipeProviderConfiguration rundownProviders[] =
- {
- { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast(EventPipeEventLevel::Verbose) }, // Public provider.
- { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast(EventPipeEventLevel::Verbose) } // Rundown provider.
- };
- // The circular buffer size doesn't matter because all events are written synchronously during rundown.
- s_pSession = s_pConfig->CreateSession(1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders);
- s_pConfig->EnableRundown(s_pSession);
-
- // Ask the runtime to emit rundown events.
- if(g_fEEStarted && !g_fEEShutDown)
+ LARGE_INTEGER disableTimeStamp;
+ QueryPerformanceCounter(&disableTimeStamp);
+ s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
+
+ if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
{
- ETW::EnumerationLog::EndRundown();
- }
+ // Before closing the file, do rundown.
+ const unsigned int numRundownProviders = 2;
+ EventPipeProviderConfiguration rundownProviders[] =
+ {
+ { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast(EventPipeEventLevel::Verbose) }, // Public provider.
+ { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast(EventPipeEventLevel::Verbose) } // Rundown provider.
+ };
+ // The circular buffer size doesn't matter because all events are written synchronously during rundown.
+ s_pSession = s_pConfig->CreateSession(EventPipeSessionType::File, 1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders);
+ s_pConfig->EnableRundown(s_pSession);
+
+ // Ask the runtime to emit rundown events.
+ if(g_fEEStarted && !g_fEEShutDown)
+ {
+ ETW::EnumerationLog::EndRundown();
+ }
- // Disable the event pipe now that rundown is complete.
- s_pConfig->Disable(s_pSession);
+ // Disable the event pipe now that rundown is complete.
+ s_pConfig->Disable(s_pSession);
- // Delete the rundown session.
- s_pConfig->DeleteSession(s_pSession);
- s_pSession = NULL;
- }
+ // Delete the rundown session.
+ s_pConfig->DeleteSession(s_pSession);
+ s_pSession = NULL;
+ }
- if(s_pFile != NULL)
- {
delete(s_pFile);
s_pFile = NULL;
}
@@ -453,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;
@@ -471,7 +503,7 @@ EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventP
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -486,6 +518,25 @@ EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventP
}
+EventPipeProvider* EventPipe::GetProvider(const SString &providerName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EventPipeProvider *pProvider = NULL;
+ if (s_pConfig != NULL)
+ {
+ pProvider = s_pConfig->GetProvider(providerName);
+ }
+
+ return pProvider;
+}
+
void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
{
CONTRACTL
@@ -554,7 +605,6 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION(s_pSession != NULL);
}
CONTRACTL_END;
@@ -577,6 +627,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
// We can't procede without a configuration
return;
}
+ _ASSERTE(s_pSession != NULL);
// If the activity id isn't specified, pull it from the current thread.
if(pActivityId == NULL)
@@ -756,7 +807,7 @@ bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackC
StackWalkAction swaRet = pThread->StackWalkFrames(
(PSTACKWALKFRAMESCALLBACK) &StackWalkCallback,
&stackContents,
- ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES);
+ ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE));
}
@@ -975,7 +1026,29 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv
#endif
}
-void QCALLTYPE EventPipeInternal::Enable(
+EventPipeEventInstance* EventPipe::GetNextEvent()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ EventPipeEventInstance *pInstance = NULL;
+
+ // Only fetch the next event if a tracing session exists.
+ // The buffer manager is not disposed until the process is shutdown.
+ if (s_pSession != NULL)
+ {
+ pInstance = s_pBufferManager->GetNextEvent();
+ }
+
+ return pInstance;
+}
+
+UINT64 QCALLTYPE EventPipeInternal::Enable(
__in_z LPCWSTR outputFile,
UINT32 circularBufferSizeInMB,
INT64 profilerSamplingRateInNanoseconds,
@@ -984,19 +1057,46 @@ 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(
@@ -1041,6 +1141,22 @@ INT_PTR QCALLTYPE EventPipeInternal::DefineEvent(
return reinterpret_cast(pEvent);
}
+INT_PTR QCALLTYPE EventPipeInternal::GetProvider(
+ __in_z LPCWSTR providerName)
+{
+ QCALL_CONTRACT;
+
+ EventPipeProvider *pProvider = NULL;
+
+ BEGIN_QCALL;
+
+ pProvider = EventPipe::GetProvider(providerName);
+
+ END_QCALL;
+
+ return reinterpret_cast(pProvider);
+}
+
void QCALLTYPE EventPipeInternal::DeleteProvider(
INT_PTR provHandle)
{
@@ -1153,4 +1269,31 @@ void QCALLTYPE EventPipeInternal::WriteEventData(
END_QCALL;
}
+bool QCALLTYPE EventPipeInternal::GetNextEvent(
+ EventPipeEventInstanceData *pInstance)
+{
+ QCALL_CONTRACT;
+
+ EventPipeEventInstance *pNextInstance = NULL;
+ BEGIN_QCALL;
+
+ _ASSERTE(pInstance != NULL);
+
+ pNextInstance = EventPipe::GetNextEvent();
+ if (pNextInstance)
+ {
+ 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();
+ }
+
+ END_QCALL;
+ return pNextInstance != NULL;
+}
+
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index 2becb5c0b876..d5273c4974be 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -12,6 +12,7 @@ class CrstStatic;
class CrawlFrame;
class EventPipeConfiguration;
class EventPipeEvent;
+class EventPipeEventInstance;
class EventPipeFile;
class EventPipeJsonFile;
class EventPipeBuffer;
@@ -214,6 +215,8 @@ class StackContents
}
};
+typedef UINT64 EventPipeSessionID;
+
class EventPipe
{
// Declare friends.
@@ -235,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();
@@ -250,6 +256,9 @@ class EventPipe
// Create a provider.
static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
+ // Get a provider.
+ static EventPipeProvider* GetProvider(const SString &providerName);
+
// Delete a provider.
static void DeleteProvider(EventPipeProvider *pProvider);
@@ -273,6 +282,9 @@ class EventPipe
// Save the command line for the current process.
static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv);
+ // Get next event.
+ static EventPipeEventInstance* GetNextEvent();
+
protected:
// The counterpart to WriteEvent which after the payload is constructed
@@ -281,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);
@@ -373,16 +385,39 @@ class EventPipeInternal
EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
};
+ struct EventPipeEventInstanceData
+ {
+ 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,
@@ -397,6 +432,9 @@ class EventPipeInternal
void *pMetadata,
UINT32 metadataLength);
+ static INT_PTR QCALLTYPE GetProvider(
+ __in_z LPCWSTR providerName);
+
static void QCALLTYPE DeleteProvider(
INT_PTR provHandle);
@@ -417,6 +455,9 @@ class EventPipeInternal
EventData *pEventData,
UINT32 eventDataCount,
LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static bool QCALLTYPE GetNextEvent(
+ EventPipeEventInstanceData *pInstance);
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp
index 5f09295e95c7..060707bb3f87 100644
--- a/src/vm/eventpipebuffermanager.cpp
+++ b/src/vm/eventpipebuffermanager.cpp
@@ -29,6 +29,7 @@ EventPipeBufferManager::EventPipeBufferManager()
m_numBuffersStolen = 0;
m_numBuffersLeaked = 0;
m_numEventsStored = 0;
+ m_numEventsDropped = 0;
m_numEventsWritten = 0;
#endif // _DEBUG
}
@@ -76,7 +77,7 @@ EventPipeBufferManager::~EventPipeBufferManager()
}
}
-EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread, unsigned int requestSize)
+EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(EventPipeSession &session, Thread *pThread, unsigned int requestSize)
{
CONTRACTL
{
@@ -133,11 +134,14 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread
}
}
+ // Only steal buffers from other threads if the session being written to is a
+ // file-based session. Streaming sessions will simply drop events.
+ // TODO: Add dropped events telemetry here.
EventPipeBuffer *pNewBuffer = NULL;
- if(!allocateNewBuffer)
+ if(!allocateNewBuffer && (session.GetSessionType() == EventPipeSessionType::File))
{
// We can't allocate a new buffer.
- // Find the oldest buffer, zero it, and re-purpose it for this thread.
+ // Find the oldest buffer, de-allocate it, and re-purpose it for this thread.
// Find the thread that contains the oldest stealable buffer, and get its list of buffers.
EventPipeBufferList *pListToStealFrom = FindThreadToStealFrom();
@@ -179,7 +183,7 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread
// Pick the base buffer size based. Debug builds have a smaller size to stress the allocate/steal path more.
unsigned int baseBufferSize =
#ifdef _DEBUG
- 5 * 1024; // 5K
+ 30 * 1024; // 30K
#else
100 * 1024; // 100K
#endif
@@ -192,6 +196,13 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread
bufferSize = requestSize;
}
+ // Don't allow the buffer size to exceed 1MB.
+ const unsigned int maxBufferSize = 1024 * 1024;
+ if(bufferSize > maxBufferSize)
+ {
+ bufferSize = maxBufferSize;
+ }
+
// EX_TRY is used here as opposed to new (nothrow) because
// the constructor also allocates a private buffer, which
// could throw, and cannot be easily checked
@@ -363,7 +374,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeSession &sessi
// to switch to preemptive mode here.
unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize();
- pBuffer = AllocateBufferForThread(pThread, requestSize);
+ pBuffer = AllocateBufferForThread(session, pThread, requestSize);
}
// Try to write the event after we allocated (or stole) a buffer.
@@ -382,6 +393,10 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeSession &sessi
{
InterlockedIncrement(&m_numEventsStored);
}
+ else
+ {
+ InterlockedIncrement(&m_numEventsDropped);
+ }
#endif // _DEBUG
return !allocNewBuffer;
}
@@ -458,6 +473,64 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
}
}
+EventPipeEventInstance* EventPipeBufferManager::GetNextEvent()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before walking the buffer list.
+ SpinLockHolder _slh(&m_lock);
+
+ // Naively walk the circular buffer, getting the event stream in timestamp order.
+ LARGE_INTEGER stopTimeStamp;
+ QueryPerformanceCounter(&stopTimeStamp);
+ while (true)
+ {
+ EventPipeEventInstance *pOldestInstance = NULL;
+ EventPipeBuffer *pOldestContainingBuffer = NULL;
+ EventPipeBufferList *pOldestContainingList = NULL;
+ SListElem *pElem = m_pPerThreadBufferList->GetHead();
+ while (pElem != NULL)
+ {
+ EventPipeBufferList *pBufferList = pElem->GetValue();
+
+ // Peek the next event out of the list.
+ EventPipeBuffer *pContainingBuffer = NULL;
+ EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer);
+ if (pNext != NULL)
+ {
+ // If it's the oldest event we've seen, then save it.
+ if ((pOldestInstance == NULL) ||
+ (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))
+ {
+ pOldestInstance = pNext;
+ pOldestContainingBuffer = pContainingBuffer;
+ pOldestContainingList = pBufferList;
+ }
+ }
+
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
+ }
+
+ if (pOldestInstance == NULL)
+ {
+ // We're done. There are no more events.
+ return NULL;
+ }
+
+ // Pop the event from the buffer.
+ pOldestContainingList->PopNextEvent(stopTimeStamp);
+
+ // Return the oldest event that hasn't yet been processed.
+ return pOldestInstance;
+ }
+}
+
void EventPipeBufferManager::DeAllocateBuffers()
{
CONTRACTL
@@ -777,25 +850,22 @@ EventPipeEventInstance* EventPipeBufferList::PopNextEvent(LARGE_INTEGER beforeTi
EventPipeBuffer *pContainingBuffer = NULL;
EventPipeEventInstance *pNext = PeekNextEvent(beforeTimeStamp, &pContainingBuffer);
- // If the event is non-NULL, pop it.
- if(pNext != NULL && pContainingBuffer != NULL)
+ // Check to see if we need to clean-up the buffer that contained the previously popped event.
+ if(pContainingBuffer->GetPrevious() != NULL)
{
- pContainingBuffer->PopNext(beforeTimeStamp);
-
- // If the buffer is not the last buffer in the list and it has been drained, de-allocate it.
- if((pContainingBuffer->GetNext() != NULL) && (pContainingBuffer->PeekNext(beforeTimeStamp) == NULL))
- {
- // This buffer must be the head node of the list.
- _ASSERTE(pContainingBuffer->GetPrevious() == NULL);
+ // Remove the previous node. The previous node should always be the head node.
EventPipeBuffer *pRemoved = GetAndRemoveHead();
- _ASSERTE(pRemoved == pContainingBuffer);
+ _ASSERTE(pRemoved != pContainingBuffer);
+ _ASSERTE(pContainingBuffer == GetHead());
// De-allocate the buffer.
m_pManager->DeAllocateBuffer(pRemoved);
+ }
- // Reset the read buffer so that it becomes the head node on next peek or pop operation.
- m_pReadBuffer = NULL;
- }
+ // If the event is non-NULL, pop it.
+ if(pNext != NULL && pContainingBuffer != NULL)
+ {
+ pContainingBuffer->PopNext(beforeTimeStamp);
}
return pNext;
diff --git a/src/vm/eventpipebuffermanager.h b/src/vm/eventpipebuffermanager.h
index 87502ed6fc1b..b72648a764fe 100644
--- a/src/vm/eventpipebuffermanager.h
+++ b/src/vm/eventpipebuffermanager.h
@@ -43,13 +43,14 @@ class EventPipeBufferManager
unsigned int m_numBuffersStolen;
unsigned int m_numBuffersLeaked;
Volatile m_numEventsStored;
+ Volatile m_numEventsDropped;
LONG m_numEventsWritten;
#endif // _DEBUG
// Allocate a new buffer for the specified thread.
// This function will store the buffer in the thread's buffer list for future use and also return it here.
// A NULL return value means that a buffer could not be allocated.
- EventPipeBuffer* AllocateBufferForThread(Thread *pThread, unsigned int requestSize);
+ EventPipeBuffer* AllocateBufferForThread(EventPipeSession &session, Thread *pThread, unsigned int requestSize);
// Add a buffer to the thread buffer list.
void AddBufferToThreadBufferList(EventPipeBufferList *pThreadBuffers, EventPipeBuffer *pBuffer);
@@ -82,6 +83,9 @@ class EventPipeBufferManager
// to free their buffer for a very long time.
void DeAllocateBuffers();
+ // Get next event. This is used to dispatch events to EventListener.
+ EventPipeEventInstance* GetNextEvent();
+
#ifdef _DEBUG
bool EnsureConsistency();
#endif // _DEBUG
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
index d787956c7c28..571b0f11b171 100644
--- a/src/vm/eventpipeconfiguration.cpp
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -86,7 +86,7 @@ void EventPipeConfiguration::Initialize()
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -108,7 +108,7 @@ EventPipeProvider* EventPipeConfiguration::CreateProvider(const SString &provide
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -151,7 +151,7 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -309,7 +309,7 @@ size_t EventPipeConfiguration::GetCircularBufferSize() const
return ret;
}
-EventPipeSession* EventPipeConfiguration::CreateSession(unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders)
+EventPipeSession* EventPipeConfiguration::CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders)
{
CONTRACTL
{
@@ -319,7 +319,7 @@ EventPipeSession* EventPipeConfiguration::CreateSession(unsigned int circularBuf
}
CONTRACTL_END;
- return new EventPipeSession(circularBufferSizeInMB, pProviders, numProviders);
+ return new EventPipeSession(sessionType, circularBufferSizeInMB, pProviders, numProviders);
}
void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
@@ -346,7 +346,7 @@ void EventPipeConfiguration::Enable(EventPipeSession *pSession)
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
PRECONDITION(pSession != NULL);
// Lock must be held by EventPipe::Enable.
@@ -385,7 +385,7 @@ void EventPipeConfiguration::Disable(EventPipeSession *pSession)
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
// TODO: Multiple session support will require that the session be specified.
PRECONDITION(pSession != NULL);
@@ -431,7 +431,7 @@ void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession)
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
PRECONDITION(pSession != NULL);
// Lock must be held by EventPipe::Disable.
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
index 0f500bd73a0b..22b5cf9cc36a 100644
--- a/src/vm/eventpipeconfiguration.h
+++ b/src/vm/eventpipeconfiguration.h
@@ -15,6 +15,7 @@ class EventPipeEventInstance;
class EventPipeProvider;
struct EventPipeProviderConfiguration;
class EventPipeSession;
+enum class EventPipeSessionType;
class EventPipeSessionProvider;
enum class EventPipeEventLevel
@@ -53,7 +54,7 @@ class EventPipeConfiguration
EventPipeProvider* GetProvider(const SString &providerID);
// Create a new session.
- EventPipeSession* CreateSession(unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders);
+ EventPipeSession* CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders);
// Delete a session.
void DeleteSession(EventPipeSession *pSession);
diff --git a/src/vm/eventpipeeventsource.cpp b/src/vm/eventpipeeventsource.cpp
index 1a5cf2a3f3dd..a115af72276e 100644
--- a/src/vm/eventpipeeventsource.cpp
+++ b/src/vm/eventpipeeventsource.cpp
@@ -20,7 +20,7 @@ EventPipeEventSource::EventPipeEventSource()
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp
index 3e827068013f..c2a0169e7ba2 100644
--- a/src/vm/eventpipeprovider.cpp
+++ b/src/vm/eventpipeprovider.cpp
@@ -116,7 +116,7 @@ void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, E
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
@@ -191,7 +191,7 @@ void EventPipeProvider::InvokeCallback()
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp
index 7fd7ac11cc96..b10caf2f5b4e 100644
--- a/src/vm/eventpipesession.cpp
+++ b/src/vm/eventpipesession.cpp
@@ -10,6 +10,7 @@
#ifdef FEATURE_PERFTRACING
EventPipeSession::EventPipeSession(
+ EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
EventPipeProviderConfiguration *pProviders,
unsigned int numProviders)
@@ -22,11 +23,14 @@ EventPipeSession::EventPipeSession(
}
CONTRACTL_END;
+ m_sessionType = sessionType;
m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB;
m_rundownEnabled = false;
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 ba91c60aaa40..5518e76097c6 100644
--- a/src/vm/eventpipesession.h
+++ b/src/vm/eventpipesession.h
@@ -12,6 +12,12 @@ struct EventPipeProviderConfiguration;
class EventPipeSessionProviderList;
class EventPipeSessionProvider;
+enum class EventPipeSessionType
+{
+ File,
+ Streaming
+};
+
class EventPipeSession
{
private:
@@ -24,10 +30,21 @@ class EventPipeSession
// True if rundown is enabled.
Volatile m_rundownEnabled;
+ // The type of the session.
+ // 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.
EventPipeSession(
+ EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
EventPipeProviderConfiguration *pProviders,
unsigned int numProviders);
@@ -37,6 +54,13 @@ class EventPipeSession
// Determine if the session is valid or not. Invalid sessions can be detected before they are enabled.
bool IsValid() const;
+ // Get the session type.
+ EventPipeSessionType GetSessionType() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_sessionType;
+ }
+
// Get the configured size of the circular buffer.
size_t GetCircularBufferSize() const
{
@@ -58,6 +82,20 @@ class EventPipeSession
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();
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 502b5ada13b6..14dca0d6a05a 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -8036,6 +8036,9 @@ void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
if (dontInline(inlineResult))
{
const char * str = (reason ? reason : "");
+ SString strReason;
+ strReason.SetANSI(str);
+
FireEtwMethodJitInliningFailed(methodBeingCompiledNames[0].GetUnicode(),
methodBeingCompiledNames[1].GetUnicode(),
@@ -8047,7 +8050,7 @@ void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
inlineeNames[1].GetUnicode(),
inlineeNames[2].GetUnicode(),
inlineResult == INLINE_NEVER,
- str,
+ strReason.GetUnicode(),
GetClrInstanceId());
}
else
@@ -8360,6 +8363,8 @@ void CEEInfo::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
if (tailCallResult == TAILCALL_FAIL)
{
const char * str = (reason ? reason : "");
+ SString strReason;
+ strReason.SetANSI(str);
FireEtwMethodJitTailCallFailed(methodBeingCompiledNames[0].GetUnicode(),
methodBeingCompiledNames[1].GetUnicode(),
@@ -8371,7 +8376,7 @@ void CEEInfo::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
calleeNames[1].GetUnicode(),
calleeNames[2].GetUnicode(),
fIsTailPrefix,
- str,
+ strReason.GetUnicode(),
GetClrInstanceId());
}
else
diff --git a/sync.sh b/sync.sh
index ab0fb578c5cb..8d8b16734f48 100755
--- a/sync.sh
+++ b/sync.sh
@@ -13,7 +13,9 @@ working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
unprocessedBuildArgs=
# Parse arguments
-if [ $# == 0 ]; then
+# Assume the default '-p' argument if the only arguments specified are specified after double dash.
+# Only position parameters can be specified after the double dash.
+if [ $# == 0 ] || [ $1 == '--' ]; then
buildArgs="-p"
fi
diff --git a/tests/issues.targets b/tests/issues.targets
index 17fe78e9a7b9..987b3b577053 100644
--- a/tests/issues.targets
+++ b/tests/issues.targets
@@ -1755,16 +1755,8 @@
needs triage
-
-
-
-
-
-
- 15494
-
-
- 15494
+
+ by design Windows only
diff --git a/tests/src/baseservices/threading/coverage/OSThreadId/OSThreadId.cs b/tests/src/baseservices/threading/coverage/OSThreadId/OSThreadId.cs
new file mode 100644
index 000000000000..85895a718952
--- /dev/null
+++ b/tests/src/baseservices/threading/coverage/OSThreadId/OSThreadId.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Threading;
+
+namespace Threading.Tests
+{
+ public sealed class OSThreadId
+ {
+ private const int NumThreads = 10;
+ private static MethodInfo s_osThreadIdGetMethod;
+ private static ManualResetEvent s_resetEvent = new ManualResetEvent(false);
+ private static ulong[] s_threadIds = new ulong[NumThreads];
+
+ public static int Main(string[] args)
+ {
+ // The property to be tested is internal.
+ Type runtimeThreadType = typeof(object).Assembly.GetType("Internal.Runtime.Augments.RuntimeThread");
+ Assert(runtimeThreadType != null);
+ PropertyInfo osThreadIdProperty = runtimeThreadType.GetProperty("CurrentOSThreadId", BindingFlags.NonPublic | BindingFlags.Static);
+ Assert(osThreadIdProperty != null);
+ s_osThreadIdGetMethod = osThreadIdProperty.GetGetMethod(true);
+ Assert(s_osThreadIdGetMethod != null);
+
+ // Test the main thread.
+ Assert(GetCurrentThreadId() > 0);
+
+ // Create more threads.
+ Thread[] threads = new Thread[NumThreads];
+ for (int i = 0; i < NumThreads; i++)
+ {
+ threads[i] = new Thread(new ParameterizedThreadStart(ThreadProc));
+ threads[i].Start(i);
+ }
+
+ // Now that all threads have been created, allow them to run.
+ s_resetEvent.Set();
+
+ // Wait for all threads to complete.
+ for (int i = 0; i < NumThreads; i++)
+ {
+ threads[i].Join();
+ }
+
+ // Check for duplicate thread IDs.
+ Array.Sort(s_threadIds);
+ ulong previousThreadId = 0;
+ for (int i = 0; i < NumThreads; i++)
+ {
+ if (i == 0)
+ {
+ previousThreadId = s_threadIds[i];
+ }
+ else
+ {
+ Assert(s_threadIds[i] > 0);
+ Assert(previousThreadId != s_threadIds[i]);
+ previousThreadId = s_threadIds[i];
+ }
+ }
+
+ return 100;
+ }
+
+ private static ulong GetCurrentThreadId()
+ {
+ return (ulong)s_osThreadIdGetMethod.Invoke(null, null);
+ }
+
+ private static void ThreadProc(object state)
+ {
+ // Get the thread index.
+ int threadIndex = (int)state;
+ Assert(threadIndex >= 0 && threadIndex < NumThreads);
+
+ // Wait for all threads to be created.
+ s_resetEvent.WaitOne();
+
+ // We now know that all threads were created before GetCurrentThread is called.
+ // Thus, no thread IDs can be duplicates.
+ ulong threadId = GetCurrentThreadId();
+
+ // Ensure that the thread ID is valid.
+ Assert(threadId > 0);
+
+ // Save the thread ID so that it can be checked for duplicates.
+ s_threadIds[threadIndex] = threadId;
+ }
+
+ private static void Assert(bool condition)
+ {
+ if (!condition)
+ {
+ throw new Exception("Assertion failed.");
+ }
+ }
+ }
+}
diff --git a/tests/src/baseservices/threading/coverage/OSThreadId/osthreadid.csproj b/tests/src/baseservices/threading/coverage/OSThreadId/osthreadid.csproj
new file mode 100644
index 000000000000..6d263047e1ef
--- /dev/null
+++ b/tests/src/baseservices/threading/coverage/OSThreadId/osthreadid.csproj
@@ -0,0 +1,31 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {8E3244CB-407F-4142-BAAB-E7A55901A5FA}
+ Exe
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ ..\..\
+ BuildAndRun
+ $(DefineConstants);STATIC
+ true
+ 0
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
diff --git a/tests/src/tracing/common/EtlFile.cs b/tests/src/tracing/common/EtlFile.cs
new file mode 100644
index 000000000000..f476ce230c8e
--- /dev/null
+++ b/tests/src/tracing/common/EtlFile.cs
@@ -0,0 +1,33 @@
+using System;
+using System.IO;
+
+namespace Tracing.Tests.Common
+{
+ public class EtlFile : IDisposable
+ {
+ public string Path { get; }
+ private bool KeepOutput { get; }
+
+ private EtlFile(string fileName, bool keep)
+ {
+ Path = fileName;
+ KeepOutput = keep;
+ }
+
+ public void Dispose()
+ {
+ if (KeepOutput)
+ Console.WriteLine("\n\tOutput file: {0}", Path);
+ else
+ File.Delete(Path);
+ }
+
+ public static EtlFile Create(string[] args)
+ {
+ if (args.Length >= 1)
+ return new EtlFile(args[0], true);
+
+ return new EtlFile(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".etl", false);
+ }
+ }
+}
diff --git a/tests/src/tracing/common/RuntimeEventSource.cs b/tests/src/tracing/common/RuntimeEventSource.cs
new file mode 100644
index 000000000000..8518187d9e0e
--- /dev/null
+++ b/tests/src/tracing/common/RuntimeEventSource.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Diagnostics.Tracing;
+using System.Reflection;
+
+namespace Tracing.Tests.Common
+{
+ public static class RuntimeEventSource
+ {
+ private static FieldInfo m_staticLogField;
+
+ public static EventSource Log
+ {
+ get
+ {
+ return (EventSource) m_staticLogField.GetValue(null);
+ }
+ }
+
+ static RuntimeEventSource()
+ {
+ if(!Initialize())
+ {
+ throw new InvalidOperationException("Reflection failed.");
+ }
+ }
+
+ private static bool Initialize()
+ {
+ Assembly SPC = typeof(System.Diagnostics.Tracing.EventSource).Assembly;
+ if(SPC == null)
+ {
+ Console.WriteLine("System.Private.CoreLib assembly == null");
+ return false;
+ }
+ Type runtimeEventSourceType = SPC.GetType("System.Diagnostics.Tracing.RuntimeEventSource");
+ if(runtimeEventSourceType == null)
+ {
+ Console.WriteLine("System.Diagnostics.Tracing.RuntimeEventSource type == null");
+ return false;
+ }
+ m_staticLogField = runtimeEventSourceType.GetField("Log", BindingFlags.NonPublic | BindingFlags.Static);
+ if(m_staticLogField == null)
+ {
+ Console.WriteLine("RuntimeEventSource.Log field == null");
+ return false;
+ }
+
+ return true;
+ }
+
+ }
+}
diff --git a/tests/src/tracing/common/common.csproj b/tests/src/tracing/common/common.csproj
index 0fe8d0fb4f37..3fb7cc1b9dd5 100644
--- a/tests/src/tracing/common/common.csproj
+++ b/tests/src/tracing/common/common.csproj
@@ -26,7 +26,9 @@
+
+
diff --git a/tests/src/tracing/eventsource/eventpipeandetw/EventPipeAndEtw.cs b/tests/src/tracing/eventsource/eventpipeandetw/EventPipeAndEtw.cs
new file mode 100644
index 000000000000..71ef01365d9e
--- /dev/null
+++ b/tests/src/tracing/eventsource/eventpipeandetw/EventPipeAndEtw.cs
@@ -0,0 +1,293 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Threading;
+using Tracing.Tests.Common;
+using System.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.Parsers;
+using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+using Microsoft.Diagnostics.Tracing.Session;
+
+namespace Tracing.Tests
+{
+ [EventSource(Name = "EventPipeAndEtwEventSource")]
+ class EventPipeAndEtwEventSource : EventSource
+ {
+ public class Keywords
+ {
+ public const EventKeywords EventPipeKeyword = (EventKeywords)0x1;
+ public const EventKeywords EtwKeyword = (EventKeywords)0x2;
+ }
+
+ public static EventPipeAndEtwEventSource Log = new EventPipeAndEtwEventSource();
+
+ private EventPipeAndEtwEventSource() : base(true) { }
+
+ [Event(1, Keywords = Keywords.EventPipeKeyword)]
+ public void Event1()
+ {
+ WriteEvent(1);
+ }
+
+ [Event(2, Keywords = Keywords.EtwKeyword)]
+ public void Event2()
+ {
+ WriteEvent(2);
+ }
+
+ [Event(3, Keywords = Keywords.EventPipeKeyword | Keywords.EtwKeyword)]
+ public void Event3()
+ {
+ WriteEvent(3);
+ }
+ }
+
+ class EventResults
+ {
+ public int Event1Count { get; private set; }
+ public int Event2Count { get; private set; }
+ public int Event3Count { get; private set; }
+
+ public void AddEvent(TraceEvent data)
+ {
+ if (data.ProviderName == EventPipeAndEtwEventSource.Log.Name)
+ {
+ if (data.EventName == "ManifestData")
+ {
+ return;
+ }
+
+ if (data.EventName == "Event1")
+ {
+ Event1Count++;
+ }
+ else if (data.EventName == "Event2")
+ {
+ Event2Count++;
+ }
+ else if (data.EventName == "Event3")
+ {
+ Event3Count++;
+ }
+ else
+ {
+ Console.WriteLine($"\tEncountered unexpected event with name '{data.EventName}'.");
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ public void Print(string header)
+ {
+ Console.WriteLine("\n\t" + header);
+ Console.WriteLine($"\t\tEvent1Count: {Event1Count}");
+ Console.WriteLine($"\t\tEvent2Count: {Event2Count}");
+ Console.WriteLine($"\t\tEvent3Count: {Event3Count}\n\n");
+ }
+ }
+
+ class EventPipeAndEtw
+ {
+ private static TraceConfiguration EventPipeGetConfig(EventSource eventSource, EventKeywords keywords, string outputFile="default.netperf")
+ {
+ // Setup the configuration values.
+ uint circularBufferMB = 1024; // 1 GB
+ uint level = 5;
+
+ // Create a new instance of EventPipeConfiguration.
+ TraceConfiguration config = new TraceConfiguration(outputFile, circularBufferMB);
+
+ // Enable the provider.
+ config.EnableProvider(eventSource.Name, (ulong)keywords, level);
+
+ return config;
+ }
+
+ private static TraceEventSession EnableETW(EventSource eventSource, EventKeywords keywords, string outputFile="default.etl")
+ {
+ outputFile = Path.GetFullPath(outputFile);
+ TraceEventSession session = new TraceEventSession("EventSourceEventPipeSession", outputFile);
+ session.EnableProvider(eventSource.Name, TraceEventLevel.Verbose, (ulong)keywords, null);
+ Thread.Sleep(200); // Calls are async.
+ return session;
+ }
+
+ private static void DisableETW(TraceEventSession traceEventSession)
+ {
+ traceEventSession.Flush();
+ Thread.Sleep(1010); // Calls are async.
+ traceEventSession.Dispose();
+ }
+
+ private static void WriteAllEvents(EventPipeAndEtwEventSource eventSource)
+ {
+ Console.WriteLine("\tStart: Write events.");
+ eventSource.Event1();
+ eventSource.Event2();
+ eventSource.Event3();
+ Console.WriteLine("\tEnd: Writing events.\n");
+ }
+
+ private static void RoundOne(string[] args)
+ {
+ using (var netPerfFile = NetPerfFile.Create(args))
+ {
+ using (var etlFile = EtlFile.Create(args))
+ {
+ Console.WriteLine("\tStart: Enable EventPipe.");
+ TraceControl.Enable(EventPipeGetConfig(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EventPipeKeyword, netPerfFile.Path));
+ Console.WriteLine("\tEnd: Enable EventPipe.\n");
+
+ Console.WriteLine("\tStart: Enable ETW.");
+ TraceEventSession etwSession = EnableETW(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EtwKeyword, etlFile.Path);
+ Console.WriteLine("\tEnd: Enable ETW.\n");
+
+ WriteAllEvents(EventPipeAndEtwEventSource.Log);
+
+ Console.WriteLine("\tStart: Disable ETW.");
+ DisableETW(etwSession);
+ Console.WriteLine("\tEnd: Disable ETW.\n");
+
+ WriteAllEvents(EventPipeAndEtwEventSource.Log);
+
+ Console.WriteLine("\tStart: Disable EventPipe.");
+ TraceControl.Disable();
+ Console.WriteLine("\tEnd: Disable EventPipe.\n");
+
+ Console.WriteLine("\tStart: Processing events from EventPipe file.");
+
+ EventResults eventPipeResults = new EventResults();
+ EventResults etwResults = new EventResults();
+
+ using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(netPerfFile.Path)).Events.GetSource())
+ {
+ trace.Dynamic.All += delegate (TraceEvent data)
+ {
+ eventPipeResults.AddEvent(data);
+ };
+
+ trace.Process();
+ }
+
+ // Validate EventPipe results.
+ eventPipeResults.Print("EventPipe Results:");
+ Assert.Equal("EventPipeEvent1Count", eventPipeResults.Event1Count, 2);
+ Assert.Equal("EventPipeEvent2Count", eventPipeResults.Event2Count, 0);
+ Assert.Equal("EventPipeEvent3Count", eventPipeResults.Event3Count, 2);
+
+ Console.WriteLine("\tEnd: Processing events from EventPipe file.\n");
+
+ Console.WriteLine("\tStart: Processing events from ETW file.");
+
+ using (var trace = new ETWTraceEventSource(etlFile.Path))
+ {
+ trace.Dynamic.All += delegate (TraceEvent data)
+ {
+ etwResults.AddEvent(data);
+ };
+
+ trace.Process();
+ }
+
+ // Validate ETW results.
+ etwResults.Print("ETW Results:");
+ Assert.Equal("EventPipeEvent1Count", etwResults.Event1Count, 0);
+ Assert.Equal("EventPipeEvent2Count", etwResults.Event2Count, 1);
+ Assert.Equal("EventPipeEvent3Count", etwResults.Event3Count, 1);
+
+ Console.WriteLine("\tEnd: Processing events from ETW file.");
+ }
+ }
+ }
+
+ private static void RoundTwo(string[] args)
+ {
+ using (var netPerfFile = NetPerfFile.Create(args))
+ {
+ using (var etlFile = EtlFile.Create(args))
+ {
+ Console.WriteLine("\tStart: Enable EventPipe.");
+ TraceControl.Enable(EventPipeGetConfig(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EventPipeKeyword, netPerfFile.Path));
+ Console.WriteLine("\tEnd: Enable EventPipe.\n");
+
+ Console.WriteLine("\tStart: Enable ETW.");
+ TraceEventSession etwSession = EnableETW(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EtwKeyword, etlFile.Path);
+ Console.WriteLine("\tEnd: Enable ETW.\n");
+
+ WriteAllEvents(EventPipeAndEtwEventSource.Log);
+
+ Console.WriteLine("\tStart: Disable EventPipe.");
+ TraceControl.Disable();
+ Console.WriteLine("\tEnd: Disable EventPipe.\n");
+
+ WriteAllEvents(EventPipeAndEtwEventSource.Log);
+
+ Console.WriteLine("\tStart: Disable ETW.");
+ DisableETW(etwSession);
+ Console.WriteLine("\tEnd: Disable ETW.\n");
+
+ Console.WriteLine("\tStart: Processing events from EventPipe file.");
+
+ EventResults eventPipeResults = new EventResults();
+ EventResults etwResults = new EventResults();
+
+ using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(netPerfFile.Path)).Events.GetSource())
+ {
+ trace.Dynamic.All += delegate (TraceEvent data)
+ {
+ eventPipeResults.AddEvent(data);
+ };
+
+ trace.Process();
+ }
+
+ // Validate EventPipe results.
+ eventPipeResults.Print("EventPipe Results:");
+ Assert.Equal("EventPipeEvent1Count", eventPipeResults.Event1Count, 1);
+ Assert.Equal("EventPipeEvent2Count", eventPipeResults.Event2Count, 0);
+ Assert.Equal("EventPipeEvent3Count", eventPipeResults.Event3Count, 1);
+
+ Console.WriteLine("\tEnd: Processing events from EventPipe file.\n");
+
+ Console.WriteLine("\tStart: Processing events from ETW file.");
+
+ using (var trace = new ETWTraceEventSource(etlFile.Path))
+ {
+ trace.Dynamic.All += delegate (TraceEvent data)
+ {
+ etwResults.AddEvent(data);
+ };
+
+ trace.Process();
+ }
+
+ // Validate ETW results.
+ etwResults.Print("ETW Results:");
+ Assert.Equal("EventPipeEvent1Count", etwResults.Event1Count, 0);
+ Assert.Equal("EventPipeEvent2Count", etwResults.Event2Count, 2);
+ Assert.Equal("EventPipeEvent3Count", etwResults.Event3Count, 2);
+
+ Console.WriteLine("\tEnd: Processing events from ETW file.");
+ }
+ }
+ }
+
+ static int Main(string[] args)
+ {
+ // This test can only run with elevation.
+ if (TraceEventSession.IsElevated() != true)
+ {
+ Console.WriteLine("Test skipped because the shell is not elevated.");
+ return 100;
+ }
+
+ RoundOne(args);
+ RoundTwo(args);
+
+
+ return 100;
+ }
+ }
+}
diff --git a/tests/src/tracing/eventsource/eventpipeandetw/eventpipeandetw.csproj b/tests/src/tracing/eventsource/eventpipeandetw/eventpipeandetw.csproj
new file mode 100644
index 000000000000..31c9137ca357
--- /dev/null
+++ b/tests/src/tracing/eventsource/eventpipeandetw/eventpipeandetw.csproj
@@ -0,0 +1,30 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {8E3244CB-407F-4142-BAAB-E7A55901A5FA}
+ Exe
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ ..\..\
+ BuildAndRun
+ $(DefineConstants);STATIC
+ true
+ 0
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/src/tracing/eventsource/eventsourcetrace/EventSourceTrace.cs b/tests/src/tracing/eventsource/eventsourcetrace/EventSourceTrace.cs
new file mode 100644
index 000000000000..78d53ceb4977
--- /dev/null
+++ b/tests/src/tracing/eventsource/eventsourcetrace/EventSourceTrace.cs
@@ -0,0 +1,112 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Tracing.Tests.Common;
+using System.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.Parsers;
+using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+
+namespace Tracing.Tests
+{
+ [EventSource(Name = "SimpleEventSource")]
+ class SimpleEventSource : EventSource
+ {
+ public SimpleEventSource() : base(true) { }
+
+ [Event(1)]
+ internal void MathResult(int x, int y, int z, string formula) { this.WriteEvent(1, x, y, z, formula); }
+ }
+
+ class EventSourceTrace
+ {
+ private static int messageIterations = 10000;
+
+ public static TraceConfiguration GetConfig(EventSource eventSource, string outputFile="default.netperf")
+ {
+ // Setup the configuration values.
+ uint circularBufferMB = 1024; // 1 GB
+ uint level = 5;//(uint)EventLevel.Informational;
+ TimeSpan profSampleDelay = TimeSpan.FromMilliseconds(1);
+
+ // Create a new instance of EventPipeConfiguration.
+ TraceConfiguration config = new TraceConfiguration(outputFile, circularBufferMB);
+ // Setup the provider values.
+ // Public provider.
+ string providerName = eventSource.Name;
+ UInt64 keywords = 0xffffffffffffffff;
+
+ // Enable the provider.
+ config.EnableProvider(providerName, keywords, level);
+
+ // Set the sampling rate.
+ config.SetSamplingRate(profSampleDelay);
+
+ return config;
+ }
+
+ static int Main(string[] args)
+ {
+ bool pass = true;
+
+ using (SimpleEventSource eventSource = new SimpleEventSource())
+ {
+ using (var netPerfFile = NetPerfFile.Create(args))
+ {
+ Console.WriteLine("\tStart: Enable tracing.");
+ TraceControl.Enable(GetConfig(eventSource, netPerfFile.Path));
+ Console.WriteLine("\tEnd: Enable tracing.\n");
+
+ Console.WriteLine("\tStart: Messaging.");
+ // Send messages
+ // Use random numbers and addition as a simple, human readble checksum
+ Random generator = new Random();
+ for (int i = 0; i < messageIterations; i++)
+ {
+ int x = generator.Next(1, 1000);
+ int y = generator.Next(1, 1000);
+ string formula = String.Format("{0} + {1} = {2}", x, y, x + y);
+
+ eventSource.MathResult(x, y, x + y, formula);
+ }
+ Console.WriteLine("\tEnd: Messaging.\n");
+
+ Console.WriteLine("\tStart: Disable tracing.");
+ TraceControl.Disable();
+ Console.WriteLine("\tEnd: Disable tracing.\n");
+
+ Console.WriteLine("\tStart: Processing events from file.");
+ int msgCount = 0;
+ using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(netPerfFile.Path)).Events.GetSource())
+ {
+ var names = new HashSet();
+
+ trace.Dynamic.All += delegate (TraceEvent data)
+ {
+ if (!names.Contains(data.ProviderName))
+ {
+ Console.WriteLine("\t{0}", data.ProviderName);
+ names.Add(data.ProviderName);
+ }
+
+ if (data.ProviderName == "SimpleEventSource")
+ {
+ msgCount += 1;
+ }
+ };
+
+ trace.Process();
+ }
+ Console.WriteLine("\tEnd: Processing events from file.\n");
+
+ Console.WriteLine("\tProcessed {0} events from EventSource", msgCount);
+
+ pass &= msgCount == messageIterations;
+ }
+ }
+
+ return pass ? 100 : 0;
+ }
+ }
+}
diff --git a/tests/src/tracing/eventsourcetrace/eventsourcetrace.csproj b/tests/src/tracing/eventsource/eventsourcetrace/eventsourcetrace.csproj
similarity index 96%
rename from tests/src/tracing/eventsourcetrace/eventsourcetrace.csproj
rename to tests/src/tracing/eventsource/eventsourcetrace/eventsourcetrace.csproj
index 0bf6d8fd3939..83415240c47d 100644
--- a/tests/src/tracing/eventsourcetrace/eventsourcetrace.csproj
+++ b/tests/src/tracing/eventsource/eventsourcetrace/eventsourcetrace.csproj
@@ -24,7 +24,7 @@
-
+
\ No newline at end of file
diff --git a/tests/src/tracing/eventsourcetrace/EventSourceTrace.cs b/tests/src/tracing/eventsourcetrace/EventSourceTrace.cs
deleted file mode 100644
index 3c9135927c41..000000000000
--- a/tests/src/tracing/eventsourcetrace/EventSourceTrace.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System;
-using System.IO;
-using System.Collections.Generic;
-using Tracing.Tests.Common;
-using System.Diagnostics.Tracing;
-using Microsoft.Diagnostics.Tracing;
-using Microsoft.Diagnostics.Tracing.Etlx;
-using Microsoft.Diagnostics.Tracing.Parsers;
-using Microsoft.Diagnostics.Tracing.Parsers.Clr;
-
-namespace Tracing.Tests
-{
- [EventSource(Name = "SimpleEventSource")]
- class SimpleEventSource : EventSource
- {
- public SimpleEventSource() : base(true) { }
-
- [Event(1)]
- internal void MathResult(int x, int y, int z, string formula) { this.WriteEvent(1, x, y, z, formula); }
- }
-
- class EventSourceTrace
- {
- private static int messageIterations = 10000;
-
- public static TraceConfiguration GetConfig(EventSource eventSource, string outputFile="default.netperf")
- {
- // Setup the configuration values.
- uint circularBufferMB = 1024; // 1 GB
- uint level = 5;//(uint)EventLevel.Informational;
- TimeSpan profSampleDelay = TimeSpan.FromMilliseconds(1);
-
- // Create a new instance of EventPipeConfiguration.
- TraceConfiguration config = new TraceConfiguration(outputFile, circularBufferMB);
- // Setup the provider values.
- // Public provider.
- string providerName = eventSource.Name;
- UInt64 keywords = 0xffffffffffffffff;
-
- // Enable the provider.
- config.EnableProvider(providerName, keywords, level);
-
- // Set the sampling rate.
- config.SetSamplingRate(profSampleDelay);
-
- return config;
- }
-
- static int Main(string[] args)
- {
- bool pass = true;
-
- SimpleEventSource eventSource = new SimpleEventSource();
-
- using (var netPerfFile = NetPerfFile.Create(args))
- {
- Console.WriteLine("\tStart: Enable tracing.");
- TraceControl.Enable(GetConfig(eventSource, netPerfFile.Path));
- Console.WriteLine("\tEnd: Enable tracing.\n");
-
- Console.WriteLine("\tStart: Messaging.");
- // Send messages
- // Use random numbers and addition as a simple, human readble checksum
- Random generator = new Random();
- for(int i=0; i();
-
- trace.Dynamic.All += delegate(TraceEvent data)
- {
- if (!names.Contains(data.ProviderName))
- {
- Console.WriteLine("\t{0}", data.ProviderName);
- names.Add(data.ProviderName);
- }
-
- if (data.ProviderName == "SimpleEventSource")
- {
- msgCount += 1;
- }
- };
-
- trace.Process();
- }
- Console.WriteLine("\tEnd: Processing events from file.\n");
-
- Console.WriteLine("\tProcessed {0} events from EventSource", msgCount);
-
- pass &= msgCount == messageIterations;
- }
-
- return pass ? 100 : 0;
- }
- }
-}
diff --git a/tests/src/tracing/runtimeeventsource/RuntimeEventSourceTest.cs b/tests/src/tracing/runtimeeventsource/RuntimeEventSourceTest.cs
new file mode 100644
index 000000000000..fdaae9ede076
--- /dev/null
+++ b/tests/src/tracing/runtimeeventsource/RuntimeEventSourceTest.cs
@@ -0,0 +1,112 @@
+#define REFLECTION
+
+using System;
+using System.IO;
+using System.Diagnostics.Tracing;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using Tracing.Tests.Common;
+
+#if REFLECTION
+using System.Reflection;
+#endif
+
+namespace Tracing.Tests
+{
+ public sealed class RuntimeEventSourceTest
+ {
+ static int Main(string[] args)
+ {
+ // Get the RuntimeEventSource.
+ EventSource eventSource = RuntimeEventSource.Log;
+
+ using (SimpleEventListener noEventsListener = new SimpleEventListener("NoEvents"))
+ {
+ // Enable the provider, but not any keywords, so we should get no events as long as no rundown occurs.
+ noEventsListener.EnableEvents(eventSource, EventLevel.Critical, (EventKeywords)(0));
+
+ // Create an EventListener.
+ using (SimpleEventListener listener = new SimpleEventListener("Simple"))
+ {
+ // Trigger the allocator task.
+ System.Threading.Tasks.Task.Run(new Action(Allocator));
+
+ // Enable events.
+ listener.EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)(0x4c14fccbd));
+
+ // Wait for events.
+ Thread.Sleep(1000);
+
+ // Generate some GC events.
+ GC.Collect(2, GCCollectionMode.Forced);
+
+ // Wait for more events.
+ Thread.Sleep(1000);
+
+ // Ensure that we've seen some events.
+ Assert.True("listener.EventCount > 0", listener.EventCount > 0);
+ }
+
+ // Generate some more GC events.
+ GC.Collect(2, GCCollectionMode.Forced);
+
+ // Ensure that we've seen no events.
+ Assert.True("noEventsListener.EventCount == 0", noEventsListener.EventCount == 0);
+ }
+
+ return 100;
+ }
+
+ private static void Allocator()
+ {
+ while (true)
+ {
+ for(int i=0; i<1000; i++)
+ GC.KeepAlive(new object());
+
+ Thread.Sleep(10);
+ }
+ }
+ }
+
+ internal sealed class SimpleEventListener : EventListener
+ {
+ private string m_name;
+
+ public SimpleEventListener(string name)
+ {
+ m_name = name;
+ }
+
+ public int EventCount { get; private set; } = 0;
+
+ protected override void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ long osThreadId = -1;
+ DateTime timeStamp;
+#if REFLECTION
+ PropertyInfo threadProperty = typeof(EventWrittenEventArgs).GetProperty("OSThreadId");
+ MethodInfo threadMethod = threadProperty.GetGetMethod();
+ osThreadId = (long)threadMethod.Invoke(eventData, null);
+ PropertyInfo timeStampProperty = typeof(EventWrittenEventArgs).GetProperty("TimeStamp");
+ MethodInfo timeStampMethod = timeStampProperty.GetGetMethod();
+ timeStamp = (DateTime)timeStampMethod.Invoke(eventData, null);
+#endif
+
+ Console.WriteLine($"[{m_name}] ThreadID = {osThreadId} ID = {eventData.EventId} Name = {eventData.EventName}");
+ Console.WriteLine($"TimeStamp: {timeStamp.ToLocalTime()}");
+ Console.WriteLine($"LocalTime: {DateTime.Now}");
+ Console.WriteLine($"Difference: {DateTime.UtcNow - timeStamp}");
+ Assert.True("timeStamp < DateTime.UtcNow", timeStamp < DateTime.UtcNow);
+ for (int i = 0; i < eventData.Payload.Count; i++)
+ {
+ string payloadString = eventData.Payload[i] != null ? eventData.Payload[i].ToString() : string.Empty;
+ Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\" Value = \"{payloadString}\"");
+ }
+ Console.WriteLine("\n");
+
+
+ EventCount++;
+ }
+ }
+}
diff --git a/tests/src/tracing/runtimeeventsource/runtimeeventsource.csproj b/tests/src/tracing/runtimeeventsource/runtimeeventsource.csproj
new file mode 100644
index 000000000000..e8ab52bc9cc5
--- /dev/null
+++ b/tests/src/tracing/runtimeeventsource/runtimeeventsource.csproj
@@ -0,0 +1,32 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {8E3244CB-407F-4142-BAAB-E7A55901A5FA}
+ Exe
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ ..\..\
+ BuildAndRun
+ $(DefineConstants);STATIC
+ true
+ 0
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
diff --git a/tests/testsUnsupportedOutsideWindows.txt b/tests/testsUnsupportedOutsideWindows.txt
index 63354810552f..98a9821ee7e8 100644
--- a/tests/testsUnsupportedOutsideWindows.txt
+++ b/tests/testsUnsupportedOutsideWindows.txt
@@ -341,3 +341,4 @@ JIT/Regression/VS-ia64-JIT/V2.0-RTM/b286991/b286991/b286991.sh
managed/Compilation/Compilation/Compilation.sh
Regressions/coreclr/0584/Test584/Test584.sh
Interop/SizeConst/SizeConstTest/SizeConstTest.sh
+tracing/eventsource/eventpipeandetw/eventpipeandetw/eventpipeandetw.sh