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 @@ + + + + + +