diff options
Diffstat (limited to 'src/mscorlib/src/System/Diagnostics/Eventing')
3 files changed, 311 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs new file mode 100644 index 0000000000..2f6fdf62ef --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs @@ -0,0 +1,175 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Win32; + +namespace System.Diagnostics.Tracing +{ + [StructLayout(LayoutKind.Sequential)] + internal struct EventPipeProviderConfiguration + { + [MarshalAs(UnmanagedType.LPWStr)] + private string m_providerName; + private UInt64 m_keywords; + private uint m_loggingLevel; + + internal EventPipeProviderConfiguration( + string providerName, + UInt64 keywords, + uint loggingLevel) + { + if(string.IsNullOrEmpty(providerName)) + { + throw new ArgumentNullException(nameof(providerName)); + } + if(loggingLevel > 5) // 5 == Verbose, the highest value in EventPipeLoggingLevel. + { + throw new ArgumentOutOfRangeException(nameof(loggingLevel)); + } + m_providerName = providerName; + m_keywords = keywords; + m_loggingLevel = loggingLevel; + } + + internal string ProviderName + { + get { return m_providerName; } + } + + internal UInt64 Keywords + { + get { return m_keywords; } + } + + internal uint LoggingLevel + { + get { return m_loggingLevel; } + } + } + + internal sealed class EventPipeConfiguration + { + private string m_outputFile; + private uint m_circularBufferSizeInMB; + private List<EventPipeProviderConfiguration> m_providers; + private TimeSpan m_minTimeBetweenSamples = TimeSpan.FromMilliseconds(1); + + internal EventPipeConfiguration( + string outputFile, + uint circularBufferSizeInMB) + { + if(string.IsNullOrEmpty(outputFile)) + { + throw new ArgumentNullException(nameof(outputFile)); + } + if(circularBufferSizeInMB == 0) + { + throw new ArgumentOutOfRangeException(nameof(circularBufferSizeInMB)); + } + m_outputFile = outputFile; + m_circularBufferSizeInMB = circularBufferSizeInMB; + m_providers = new List<EventPipeProviderConfiguration>(); + } + + internal string OutputFile + { + get { return m_outputFile; } + } + + internal uint CircularBufferSizeInMB + { + get { return m_circularBufferSizeInMB; } + } + + internal EventPipeProviderConfiguration[] Providers + { + get { return m_providers.ToArray(); } + } + + internal long ProfilerSamplingRateInNanoseconds + { + // 100 nanoseconds == 1 tick. + get { return m_minTimeBetweenSamples.Ticks * 100; } + } + + internal void EnableProvider(string providerName, UInt64 keywords, uint loggingLevel) + { + m_providers.Add(new EventPipeProviderConfiguration( + providerName, + keywords, + loggingLevel)); + } + + internal void SetProfilerSamplingRate(TimeSpan minTimeBetweenSamples) + { + if(minTimeBetweenSamples.Ticks <= 0) + { + throw new ArgumentOutOfRangeException(nameof(minTimeBetweenSamples)); + } + + m_minTimeBetweenSamples = minTimeBetweenSamples; + } + } + + internal static class EventPipe + { + internal static void Enable(EventPipeConfiguration configuration) + { + if(configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + EventPipeProviderConfiguration[] providers = configuration.Providers; + + EventPipeInternal.Enable( + configuration.OutputFile, + configuration.CircularBufferSizeInMB, + configuration.ProfilerSamplingRateInNanoseconds, + providers, + providers.Length); + } + + internal static void Disable() + { + EventPipeInternal.Disable(); + } + } + + internal static class EventPipeInternal + { + // + // These PInvokes are used by the configuration APIs to interact with EventPipe. + // + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void Disable(); + + // + // These PInvokes are used by EventSource to interact with the EventPipe. + // + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern IntPtr CreateProvider(Guid providerID, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, Int64 keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void DeleteProvider(IntPtr provHandle); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern unsafe void WriteEvent(IntPtr eventHandle, uint eventID, void* pData, uint length, Guid* activityId, Guid* relatedActivityId); + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs new file mode 100644 index 0000000000..d5bc4c2889 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs @@ -0,0 +1,116 @@ +// 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.Concurrent; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Win32; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Diagnostics.Tracing +{ + internal sealed class EventPipeEventProvider : IEventProvider + { + // The EventPipeProvider handle. + private IntPtr m_provHandle = IntPtr.Zero; + + // Register an event provider. + unsafe uint IEventProvider.EventRegister( + ref Guid providerId, + UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, + void* callbackContext, + ref long registrationHandle) + { + uint returnStatus = 0; + m_provHandle = EventPipeInternal.CreateProvider(providerId, enableCallback); + if(m_provHandle != IntPtr.Zero) + { + // Fixed registration handle because a new EventPipeEventProvider + // will be created for each new EventSource. + registrationHandle = 1; + } + else + { + // Unable to create the provider. + returnStatus = 1; + } + + return returnStatus; + } + + // Unregister an event provider. + uint IEventProvider.EventUnregister(long registrationHandle) + { + EventPipeInternal.DeleteProvider(m_provHandle); + return 0; + } + + // Write an event. + unsafe int IEventProvider.EventWriteTransferWrapper( + long registrationHandle, + ref EventDescriptor eventDescriptor, + IntPtr eventHandle, + Guid* activityId, + Guid* relatedActivityId, + int userDataCount, + EventProvider.EventData* userData) + { + uint eventID = (uint)eventDescriptor.EventId; + if(eventID != 0 && eventHandle != IntPtr.Zero) + { + if (userDataCount == 0) + { + EventPipeInternal.WriteEvent(eventHandle, eventID, null, 0, activityId, relatedActivityId); + return 0; + } + + uint length = 0; + for (int i = 0; i < userDataCount; i++) + { + length += userData[i].Size; + } + + byte[] data = new byte[length]; + fixed (byte *pData = data) + { + uint offset = 0; + for (int i = 0; i < userDataCount; i++) + { + byte * singleUserDataPtr = (byte *)(userData[i].Ptr); + uint singleUserDataSize = userData[i].Size; + WriteToBuffer(pData, length, ref offset, singleUserDataPtr, singleUserDataSize); + } + EventPipeInternal.WriteEvent(eventHandle, eventID, pData, length, activityId, relatedActivityId); + } + } + return 0; + } + + // Get or set the per-thread activity ID. + int IEventProvider.EventActivityIdControl(UnsafeNativeMethods.ManifestEtw.ActivityControl ControlCode, ref Guid ActivityId) + { + return 0; + } + + // Define an EventPipeEvent handle. + unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, Int64 keywords, uint eventVersion, uint level, byte *pMetadata, uint metadataLength) + { + IntPtr eventHandlePtr = EventPipeInternal.DefineEvent(m_provHandle, eventID, keywords, eventVersion, level, pMetadata, metadataLength); + return eventHandlePtr; + } + + // Copy src to buffer and modify the offset. + // Note: We know the buffer size ahead of time to make sure no buffer overflow. + private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength) + { + Debug.Assert(bufferLength >= (offset + srcLength)); + for (int i = 0; i < srcLength; i++) + { + *(byte *)(buffer + offset + i) = *(byte *)(src + i); + } + offset += srcLength; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs index b691dd38b9..01aac72cf6 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs @@ -11,6 +11,13 @@ namespace System.Diagnostics.Tracing { public partial class EventSource { +#if FEATURE_MANAGED_ETW && FEATURE_PERFTRACING + // For non-Windows, we use a thread-local variable to hold the activity ID. + // On Windows, ETW has it's own thread-local variable and we participate in its use. + [ThreadStatic] + private static Guid s_currentThreadActivityId; +#endif // FEATURE_MANAGED_ETW && FEATURE_PERFTRACING + // ActivityID support (see also WriteEventWithRelatedActivityIdCore) /// <summary> /// When a thread starts work that is on behalf of 'something else' (typically another @@ -40,9 +47,13 @@ namespace System.Diagnostics.Tracing // We ignore errors to keep with the convention that EventSources do not throw errors. // Note we can't access m_throwOnWrites because this is a static method. +#if FEATURE_PERFTRACING + s_currentThreadActivityId = activityId; +#elif PLATFORM_WINDOWS if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl( UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID, ref activityId) == 0) +#endif // FEATURE_PERFTRACING { #if FEATURE_ACTIVITYSAMPLING var activityDying = s_activityDying; @@ -86,9 +97,14 @@ namespace System.Diagnostics.Tracing // We ignore errors to keep with the convention that EventSources do not throw errors. // Note we can't access m_throwOnWrites because this is a static method. +#if FEATURE_PERFTRACING + oldActivityThatWillContinue = s_currentThreadActivityId; + s_currentThreadActivityId = activityId; +#elif PLATFORM_WINDOWS UnsafeNativeMethods.ManifestEtw.EventActivityIdControl( UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID, ref oldActivityThatWillContinue); +#endif // FEATURE_PERFTRACING #endif // FEATURE_MANAGED_ETW // We don't call the activityDying callback here because the caller has declared that @@ -108,9 +124,13 @@ namespace System.Diagnostics.Tracing // errors. Note we can't access m_throwOnWrites because this is a static method. Guid retVal = new Guid(); #if FEATURE_MANAGED_ETW +#if FEATURE_PERFTRACING + retVal = s_currentThreadActivityId; +#elif PLATFORM_WINDOWS UnsafeNativeMethods.ManifestEtw.EventActivityIdControl( UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID, ref retVal); +#endif // FEATURE_PERFTRACING #endif // FEATURE_MANAGED_ETW return retVal; } |