diff --git a/src/inc/eventtracebase.h b/src/inc/eventtracebase.h index 7c0ee78b8728..26287ab54a28 100644 --- a/src/inc/eventtracebase.h +++ b/src/inc/eventtracebase.h @@ -10,7 +10,7 @@ // // -// +// // #EventTracing // Windows // ETW (Event Tracing for Windows) is a high-performance, low overhead and highly scalable @@ -35,7 +35,7 @@ void InitializeEventTracing(); // The flags must match those in the ETW manifest exactly // !!!!!!! NOTE !!!!!!!! -// These flags need to be defined either when FEATURE_EVENT_TRACE is enabled or the +// These flags need to be defined either when FEATURE_EVENT_TRACE is enabled or the // PROFILING_SUPPORTED is set, since they are used both by event tracing and profiling. enum EtwTypeFlags @@ -106,7 +106,7 @@ enum EtwThreadFlags #define ETW_TRACING_CATEGORY_ENABLED(Context, Level, Keyword) (EventPipeHelper::Enabled() || XplatEventLogger::IsEventLoggingEnabled()) #define ETW_PROVIDER_ENABLED(ProviderSymbol) (TRUE) #else //defined(FEATURE_PERFTRACING) -#define ETW_INLINE +#define ETW_INLINE #define ETWOnStartup(StartEventName, EndEventName) #define ETWFireEvent(EventName) @@ -242,13 +242,15 @@ class EventPipeHelper #if defined(FEATURE_EVENT_TRACE) +struct EventFilterDescriptor; + VOID EventPipeEtwCallbackDotNETRuntimeStress( _In_ LPCGUID SourceId, _In_ ULONG ControlCode, _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext); VOID EventPipeEtwCallbackDotNETRuntime( @@ -257,7 +259,7 @@ VOID EventPipeEtwCallbackDotNETRuntime( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext); VOID EventPipeEtwCallbackDotNETRuntimeRundown( @@ -266,7 +268,7 @@ VOID EventPipeEtwCallbackDotNETRuntimeRundown( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext); VOID EventPipeEtwCallbackDotNETRuntimePrivate( @@ -275,7 +277,7 @@ VOID EventPipeEtwCallbackDotNETRuntimePrivate( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext); #ifndef FEATURE_PAL @@ -332,7 +334,7 @@ extern "C" { #include "clretwallmain.h" -#endif // FEATURE_EVENT_TRACE +#endif // FEATURE_EVENT_TRACE /**************************/ /* CLR ETW infrastructure */ @@ -351,7 +353,7 @@ extern "C" { // has started, one may want to do something useful when that happens (e.g enumerate all the loaded modules // in the system). To enable this, we have to implement a callback routine. // file:../VM/eventtrace.cpp#EtwCallback is CLR's implementation of the callback. -// +// #include "daccess.h" class Module; @@ -379,7 +381,7 @@ namespace ETW { // Class to wrap the ETW infrastructure logic #if !defined(FEATURE_PAL) - class CEtwTracer + class CEtwTracer { #if defined(FEATURE_EVENT_TRACE) ULONG RegGuids(LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle); @@ -391,7 +393,7 @@ namespace ETW HRESULT Register(); // Unregisters all the Event Tracing providers - HRESULT UnRegister(); + HRESULT UnRegister(); #else HRESULT Register() { @@ -406,7 +408,7 @@ namespace ETW #endif // !defined(FEATURE_PAL) class LoaderLog; - class MethodLog; + class MethodLog; // Class to wrap all the enumeration logic for ETW class EnumerationLog { @@ -447,7 +449,7 @@ namespace ETW MethodDCEndILToNativeMap= 0x00020000, JitMethodILToNativeMap= 0x00040000, TypeUnload= 0x00080000, - + // Helpers ModuleRangeEnabledAny = ModuleRangeLoad | ModuleRangeDCStart | ModuleRangeDCEnd | ModuleRangeLoadPrivate, JitMethodLoadOrDCStartAny = JitMethodLoad | JitMethodDCStart | MethodDCStartILToNativeMap, @@ -475,7 +477,7 @@ namespace ETW { #if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) public: - typedef enum _EtwStackWalkStatus + typedef enum _EtwStackWalkStatus { Completed = 0, UnInitialized = 1, @@ -492,7 +494,7 @@ namespace ETW EtwStackWalkStatus GetCurrentThreadsCallStack(UINT32 *frameCount, PVOID **Stack); #endif // FEATURE_EVENT_TRACE && !defined(FEATURE_PAL) }; - + // Class to wrap all Loader logic for ETW class LoaderLog { @@ -537,9 +539,9 @@ namespace ETW }RangeFlags; }LoaderStructs; - + static VOID DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName=NULL); - + static VOID DomainLoad(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName = NULL) { if (ETW_PROVIDER_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER)) @@ -733,7 +735,7 @@ namespace ETW { public: typedef union _BinderStructs { - typedef enum _NGENBINDREJECT_REASON { + typedef enum _NGENBINDREJECT_REASON { NGEN_BIND_START_BIND = 0, NGEN_BIND_NO_INDEX = 1, NGEN_BIND_SYSTEM_ASSEMBLY_NOT_AVAILABLE = 2, @@ -797,19 +799,19 @@ namespace ETW IsCLSCompliant=0x10 }ExceptionThrownFlags; }ExceptionStructs; - }; + }; // Class to wrap all Contention logic for ETW class ContentionLog { public: - typedef union _ContentionStructs + typedef union _ContentionStructs { - typedef enum _ContentionFlags { + typedef enum _ContentionFlags { ManagedContention=0, NativeContention=1 } ContentionFlags; } ContentionStructs; - }; + }; // Class to wrap all Interop logic for ETW class InteropLog { @@ -820,7 +822,7 @@ namespace ETW class InfoLog { public: - typedef union _InfoStructs + typedef union _InfoStructs { typedef enum _StartupMode { @@ -961,7 +963,7 @@ class ETWTraceStartup { // "mc.exe -MOF" already generates this block for XP-suported builds inside ClrEtwAll.h; // on Vista+ builds, mc is run without -MOF, and we still have code that depends on it, so // we manually place it here. -FORCEINLINE +FORCEINLINE BOOLEAN __stdcall McGenEventTracingEnabled( __in PMCGEN_TRACE_CONTEXT EnableInfo, @@ -1056,7 +1058,7 @@ struct CallStackFrame #endif // FEATURE_EVENT_TRACE #if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) -FORCEINLINE +FORCEINLINE BOOLEAN __stdcall McGenEventProviderEnabled( __in PMCGEN_TRACE_CONTEXT Context, diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs index 6262e3df4633..1a44c2883a29 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -280,6 +280,19 @@ [In] void* callbackContext m_allKeywordMask = allKeyword; List> sessionsChanged = GetSessions(); + + // The GetSessions() logic was here to support the idea that different ETW sessions + // could have different user-defined filters. (I believe it is currently broken but that is another matter.) + // However in particular GetSessions() does not support EventPipe, only ETW, which is + // the immediate problem. We work-around establishing the invariant that we always get a + // OnControllerCallback under all circumstances, even if we can't find a delta in the + // ETW logic. This fixes things for the EventPipe case. + // + // All this session based logic should be reviewed and likely removed, but that is a larger + // change that needs more careful staging. + if (sessionsChanged.Count == 0) + sessionsChanged.Add(new Tuple(new SessionInfo(0, 0), true)); + foreach (var session in sessionsChanged) { int sessionChanged = session.Item1.sessionIdBit; diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs index 456d301ed1d8..1a5de88bd1aa 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs @@ -40,10 +40,14 @@ internal struct EventPipeProviderConfiguration private ulong m_keywords; private uint m_loggingLevel; + [MarshalAs(UnmanagedType.LPWStr)] + private readonly string m_filterData; + internal EventPipeProviderConfiguration( string providerName, ulong keywords, - uint loggingLevel) + uint loggingLevel, + string filterData) { if(string.IsNullOrEmpty(providerName)) { @@ -56,6 +60,7 @@ internal EventPipeProviderConfiguration( m_providerName = providerName; m_keywords = keywords; m_loggingLevel = loggingLevel; + m_filterData = filterData; } internal string ProviderName @@ -72,6 +77,8 @@ internal uint LoggingLevel { get { return m_loggingLevel; } } + + internal string FilterData => m_filterData; } internal sealed class EventPipeConfiguration @@ -126,11 +133,17 @@ internal long ProfilerSamplingRateInNanoseconds } internal void EnableProvider(string providerName, ulong keywords, uint loggingLevel) + { + EnableProviderWithFilter(providerName, keywords, loggingLevel, null); + } + + internal void EnableProviderWithFilter(string providerName, ulong keywords, uint loggingLevel, string filterData) { m_providers.Add(new EventPipeProviderConfiguration( providerName, keywords, - loggingLevel)); + loggingLevel, + filterData)); } private void EnableProviderConfiguration(EventPipeProviderConfiguration providerConfig) diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeController.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeController.cs index e654feadc8fc..2e584768658a 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeController.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeController.cs @@ -5,12 +5,10 @@ using Internal.IO; using Microsoft.Win32; using System.IO; -using System.Globalization; using System.Reflection; using System.Runtime.Versioning; using System.Text; using System.Threading; -using System.Threading.Tasks; namespace System.Diagnostics.Tracing { @@ -54,9 +52,9 @@ internal sealed class EventPipeController // The default set of providers/keywords/levels. Used if an alternative configuration is not specified. private static readonly EventPipeProviderConfiguration[] DefaultProviderConfiguration = new EventPipeProviderConfiguration[] { - new EventPipeProviderConfiguration("Microsoft-Windows-DotNETRuntime", 0x4c14fccbd, 5), - new EventPipeProviderConfiguration("Microsoft-Windows-DotNETRuntimePrivate", 0x4002000b, 5), - new EventPipeProviderConfiguration("Microsoft-DotNETCore-SampleProfiler", 0x0, 5) + new EventPipeProviderConfiguration("Microsoft-Windows-DotNETRuntime", 0x4c14fccbd, 5, null), + new EventPipeProviderConfiguration("Microsoft-Windows-DotNETRuntimePrivate", 0x4002000b, 5, null), + new EventPipeProviderConfiguration("Microsoft-DotNETCore-SampleProfiler", 0x0, 5, null), }; // Singleton controller instance. @@ -168,8 +166,11 @@ private static EventPipeConfiguration BuildConfigFromFile(string configFilePath) foreach (string configEntry in configEntries) { //`Split the key and value by '='. - string[] entryComponents = configEntry.Split(ConfigEntryDelimiter); - if(entryComponents.Length == 2) + string[] entryComponents = configEntry.Split( + ConfigEntryDelimiter, + 2, // Stop split on first occurrence of the separator. + StringSplitOptions.RemoveEmptyEntries); + if (entryComponents.Length == 2) { string key = entryComponents[0]; if (key.Equals(ConfigKey_Providers)) @@ -225,7 +226,7 @@ private static EventPipeConfiguration BuildConfigFromFile(string configFilePath) // Get the circular buffer size. uint circularMB = DefaultCircularBufferMB; - if(!string.IsNullOrEmpty(strCircularMB)) + if (!string.IsNullOrEmpty(strCircularMB)) { circularMB = Convert.ToUInt32(strCircularMB); } @@ -313,33 +314,48 @@ private static void SetProviderConfiguration(string strConfig, EventPipeConfigur throw new ArgumentNullException(nameof(strConfig)); } - // String must be of the form "providerName:keywords:level,providerName:keywords:level..." - string[] providers = strConfig.Split(ProviderConfigDelimiter); + // Provider format: "(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]" + // where KeyValueArgs are of the form: "[key1=value1][;key2=value2]" + // `strConfig` must be of the form "Provider[,Provider]" + string[] providers = strConfig.Split( + ProviderConfigDelimiter, + StringSplitOptions.RemoveEmptyEntries); // Remove "empty" providers. foreach (string provider in providers) { - string[] components = provider.Split(ConfigComponentDelimiter); - if (components.Length == 3) + // Split expecting a maximum of four tokens. + string[] components = provider.Split( + ConfigComponentDelimiter, + 4, // if there is ':' in the parameters then anything after it will not be ignored. + StringSplitOptions.None); // Keep empty tokens + + string providerName = components.Length > 0 ? components[0] : null; + if (string.IsNullOrEmpty(providerName)) + continue; // No provider name specified. + + ulong keywords = ulong.MaxValue; + if (components.Length > 1) { - string providerName = components[0]; - // We use a try/catch block here because ulong.TryParse won't accept 0x at the beginning // of a hex string. Thus, we either need to conditionally strip it or handle the exception. // Given that this is not a perf-critical path, catching the exception is the simpler code. - ulong keywords = 0; try { keywords = Convert.ToUInt64(components[1], 16); } - catch { } - - uint level; - if (!uint.TryParse(components[2], out level)) + catch { - level = 0; } + } - config.EnableProvider(providerName, keywords, level); + uint level = 5; // Verbose + if (components.Length > 2) + { + uint.TryParse(components[2], out level); } + + string filterData = components.Length > 3 ? components[3] : null; + + config.EnableProviderWithFilter(providerName, keywords, level, filterData); } } diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs index 5e6a5596e080..a5b592293249 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventDispatcher.cs @@ -107,7 +107,7 @@ private void CommitDispatchConfiguration() // Enable the EventPipe session. EventPipeProviderConfiguration[] providerConfiguration = new EventPipeProviderConfiguration[] { - new EventPipeProviderConfiguration(RuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel) + new EventPipeProviderConfiguration(RuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel, null) }; m_sessionID = EventPipeInternal.Enable(null, 1024, 1, providerConfiguration, 1, 0); diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index 5b3904234948..c8d61dab1e3d 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -445,8 +445,8 @@ void EventPipe::Disable(EventPipeSessionID id) 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. + { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast(EventPipeEventLevel::Verbose), NULL }, // Public provider. + { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast(EventPipeEventLevel::Verbose), NULL } // 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); @@ -1207,7 +1207,7 @@ INT_PTR QCALLTYPE EventPipeInternal::GetProvider( EventPipeProvider *pProvider = NULL; BEGIN_QCALL; - + pProvider = EventPipe::GetProvider(providerName); END_QCALL; @@ -1352,6 +1352,6 @@ bool QCALLTYPE EventPipeInternal::GetNextEvent( END_QCALL; return pNextInstance != NULL; -} +} #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index a3be6ff43c7b..0ff2135ce9ad 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -24,6 +24,27 @@ class SampleProfilerEventInstance; struct EventPipeProviderConfiguration; class EventPipeSession; +// EVENT_FILTER_DESCRIPTOR (This type does not exist on non-Windows platforms.) +// https://docs.microsoft.com/en-us/windows/desktop/api/evntprov/ns-evntprov-_event_filter_descriptor +// The structure supplements the event provider, level, and keyword data that +// determines which events are reported and traced. The structure gives the +// event provider greater control over the selection of events for reporting +// and tracing. +struct EventFilterDescriptor +{ + // A pointer to the filter data. + ULONGLONG Ptr; + + // The size of the filter data, in bytes. The maximum size is 1024 bytes. + ULONG Size; + + // The type of filter data. The type is application-defined. An event + // controller that knows about the provider and knows details about the + // provider's events can use the Type field to send the provider an + // arbitrary set of data for use as enhancements to the filtering of events. + ULONG Type; +}; + // Define the event pipe callback to match the ETW callback signature. typedef void (*EventPipeCallback)( LPCGUID SourceID, @@ -31,7 +52,7 @@ typedef void (*EventPipeCallback)( UCHAR Level, ULONGLONG MatchAnyKeywords, ULONGLONG MatchAllKeywords, - void *FilterData, + EventFilterDescriptor *FilterData, void *CallbackContext); struct EventData @@ -349,6 +370,7 @@ struct EventPipeProviderConfiguration LPCWSTR m_pProviderName; UINT64 m_keywords; UINT32 m_loggingLevel; + LPCWSTR m_pFilterData; public: @@ -358,17 +380,20 @@ struct EventPipeProviderConfiguration m_pProviderName = NULL; m_keywords = NULL; m_loggingLevel = 0; + m_pFilterData = NULL; } EventPipeProviderConfiguration( LPCWSTR pProviderName, UINT64 keywords, - UINT32 loggingLevel) + UINT32 loggingLevel, + LPCWSTR pFilterData) { LIMITED_METHOD_CONTRACT; m_pProviderName = pProviderName; m_keywords = keywords; m_loggingLevel = loggingLevel; + m_pFilterData = pFilterData; } LPCWSTR GetProviderName() const @@ -388,6 +413,12 @@ struct EventPipeProviderConfiguration LIMITED_METHOD_CONTRACT; return m_loggingLevel; } + + LPCWSTR GetFilterData() const + { + LIMITED_METHOD_CONTRACT; + return m_pFilterData; + } }; class EventPipeInternal @@ -476,7 +507,7 @@ class EventPipeInternal LPCGUID pActivityId, LPCGUID pRelatedActivityId); static bool QCALLTYPE GetNextEvent( - EventPipeEventInstanceData *pInstance); + EventPipeEventInstanceData *pInstance); }; #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp index 6199748caf3c..d40f2b789e9e 100644 --- a/src/vm/eventpipeconfiguration.cpp +++ b/src/vm/eventpipeconfiguration.cpp @@ -182,7 +182,8 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider) provider.SetConfiguration( true /* providerEnabled */, pSessionProvider->GetKeywords(), - pSessionProvider->GetLevel()); + pSessionProvider->GetLevel(), + pSessionProvider->GetFilterData()); } } @@ -372,7 +373,8 @@ void EventPipeConfiguration::Enable(EventPipeSession *pSession) pProvider->SetConfiguration( true /* providerEnabled */, pSessionProvider->GetKeywords(), - pSessionProvider->GetLevel()); + pSessionProvider->GetLevel(), + pSessionProvider->GetFilterData()); } pElem = m_pProviderList->GetNext(pElem); @@ -402,7 +404,11 @@ void EventPipeConfiguration::Disable(EventPipeSession *pSession) while(pElem != NULL) { EventPipeProvider *pProvider = pElem->GetValue(); - pProvider->SetConfiguration(false /* providerEnabled */, 0 /* keywords */, EventPipeEventLevel::Critical /* level */); + pProvider->SetConfiguration( + false /* providerEnabled */, + 0 /* keywords */, + EventPipeEventLevel::Critical /* level */, + NULL /* filterData */); pElem = m_pProviderList->GetNext(pElem); } diff --git a/src/vm/eventpipeeventsource.cpp b/src/vm/eventpipeeventsource.cpp index a115af72276e..20dc5b268634 100644 --- a/src/vm/eventpipeeventsource.cpp +++ b/src/vm/eventpipeeventsource.cpp @@ -93,7 +93,8 @@ void EventPipeEventSource::Enable(EventPipeSession *pSession) EventPipeSessionProvider *pSessionProvider = new EventPipeSessionProvider( s_pProviderName, -1, - EventPipeEventLevel::LogAlways); + EventPipeEventLevel::LogAlways, + NULL); pSession->AddSessionProvider(pSessionProvider); } diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp index c2a0169e7ba2..4630c93acd41 100644 --- a/src/vm/eventpipeprovider.cpp +++ b/src/vm/eventpipeprovider.cpp @@ -111,7 +111,7 @@ bool EventPipeProvider::EventEnabled(INT64 keywords, EventPipeEventLevel eventLe ((eventLevel == EventPipeEventLevel::LogAlways) || (m_providerLevel >= eventLevel))); } -void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel) +void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel, LPCWSTR pFilterData) { CONTRACTL { @@ -127,7 +127,7 @@ void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, E m_providerLevel = providerLevel; RefreshAllEvents(); - InvokeCallback(); + InvokeCallback(pFilterData); } EventPipeEvent* EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, BYTE *pMetadata, unsigned int metadataLength) @@ -186,7 +186,7 @@ void EventPipeProvider::AddEvent(EventPipeEvent &event) event.RefreshState(); } -void EventPipeProvider::InvokeCallback() +void EventPipeProvider::InvokeCallback(LPCWSTR pFilterData) { CONTRACTL { @@ -197,6 +197,33 @@ void EventPipeProvider::InvokeCallback() } CONTRACTL_END; + + bool isEventFilterDescriptorInitialized = false; + EventFilterDescriptor eventFilterDescriptor{}; + CQuickArrayBase buffer; + buffer.Init(); + + if (pFilterData != NULL) + { + // The callback is expecting that filter data to be a concatenated list + // of pairs of null terminated strings. The first member of the pair is + // the key and the second is the value. + // To convert to this format we need to convert all '=' and ';' + // characters to '\0'. + SString dstBuffer; + SString(pFilterData).ConvertToUTF8(dstBuffer); + + const COUNT_T BUFFER_SIZE = dstBuffer.GetCount() + 1; + buffer.AllocThrows(BUFFER_SIZE); + for (COUNT_T i = 0; i < BUFFER_SIZE; ++i) + buffer[i] = (dstBuffer[i] == '=' || dstBuffer[i] == ';') ? '\0' : dstBuffer[i]; + + eventFilterDescriptor.Ptr = reinterpret_cast(buffer.Ptr()); + eventFilterDescriptor.Size = static_cast(BUFFER_SIZE); + eventFilterDescriptor.Type = 0; // EventProvider.cs: `internal enum ControllerCommand.Update` + isEventFilterDescriptorInitialized = true; + } + if(m_pCallbackFunction != NULL && !g_fEEShutDown) { (*m_pCallbackFunction)( @@ -205,9 +232,11 @@ void EventPipeProvider::InvokeCallback() (UCHAR) m_providerLevel, m_keywords, 0 /* matchAllKeywords */, - NULL /* FilterData */, + isEventFilterDescriptorInitialized ? &eventFilterDescriptor : NULL, m_pCallbackData /* CallbackContext */); } + + buffer.Destroy(); } bool EventPipeProvider::GetDeleteDeferred() const diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h index 0ffe46f887af..dffdc7e3dbbe 100644 --- a/src/vm/eventpipeprovider.h +++ b/src/vm/eventpipeprovider.h @@ -13,16 +13,6 @@ class EventPipeEvent; -// Define the event pipe callback to match the ETW callback signature. -typedef void (*EventPipeCallback)( - LPCGUID SourceID, - ULONG IsEnabled, - UCHAR Level, - ULONGLONG MatchAnyKeywords, - ULONGLONG MatchAllKeywords, - void *FilterData, - void *CallbackContext); - class EventPipeProvider { // Declare friends. @@ -96,13 +86,13 @@ class EventPipeProvider // Set the provider configuration (enable and disable sets of events). // This is called by EventPipeConfiguration. - void SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel); + void SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel, LPCWSTR pFilterData); // Refresh the runtime state of all events. void RefreshAllEvents(); // Invoke the provider callback. - void InvokeCallback(); + void InvokeCallback(LPCWSTR pFilterData); // Specifies whether or not the provider was deleted, but that deletion // was deferred until after tracing is stopped. diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp index de62f58ed9ba..7388472c7d7f 100644 --- a/src/vm/eventpipesession.cpp +++ b/src/vm/eventpipesession.cpp @@ -111,14 +111,15 @@ EventPipeSessionProviderList::EventPipeSessionProviderList( // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose. if((wcscmp(W("*"), pConfig->GetProviderName()) == 0) && (pConfig->GetKeywords() == 0xFFFFFFFFFFFFFFFF) && ((EventPipeEventLevel)pConfig->GetLevel() == EventPipeEventLevel::Verbose) && (m_pCatchAllProvider == NULL)) { - m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose); + m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose, NULL); } else { EventPipeSessionProvider *pProvider = new EventPipeSessionProvider( pConfig->GetProviderName(), pConfig->GetKeywords(), - (EventPipeEventLevel)pConfig->GetLevel()); + (EventPipeEventLevel)pConfig->GetLevel(), + pConfig->GetFilterData()); m_pProviders->InsertTail(new SListElem(pProvider)); } @@ -225,7 +226,8 @@ bool EventPipeSessionProviderList::IsEmpty() const EventPipeSessionProvider::EventPipeSessionProvider( LPCWSTR providerName, UINT64 keywords, - EventPipeEventLevel loggingLevel) + EventPipeEventLevel loggingLevel, + LPCWSTR filterData) { CONTRACTL { @@ -248,6 +250,16 @@ EventPipeSessionProvider::EventPipeSessionProvider( m_keywords = keywords; m_loggingLevel = loggingLevel; + if(filterData != NULL) + { + size_t bufSize = wcslen(filterData) + 1; + m_pFilterData = new WCHAR[bufSize]; + wcscpy_s(m_pFilterData, bufSize, filterData); + } + else + { + m_pFilterData = NULL; + } } EventPipeSessionProvider::~EventPipeSessionProvider() @@ -260,11 +272,12 @@ EventPipeSessionProvider::~EventPipeSessionProvider() } CONTRACTL_END; - if(m_pProviderName != NULL) - { - delete[] m_pProviderName; - m_pProviderName = NULL; - } + // C++ standard, $5.3.5/2: Deleting a NULL pointer is safe. + delete[] m_pProviderName; + m_pProviderName = NULL; + + delete[] m_pFilterData; + m_pFilterData = NULL; } LPCWSTR EventPipeSessionProvider::GetProviderName() const @@ -285,4 +298,10 @@ EventPipeEventLevel EventPipeSessionProvider::GetLevel() const return m_loggingLevel; } +LPCWSTR EventPipeSessionProvider::GetFilterData() const +{ + LIMITED_METHOD_CONTRACT; + return m_pFilterData; +} + #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h index 01e83b25ed1f..3c4f293407e5 100644 --- a/src/vm/eventpipesession.h +++ b/src/vm/eventpipesession.h @@ -26,7 +26,7 @@ class EventPipeSession // The configured size of the circular buffer. size_t m_circularBufferSizeInBytes; - + // True if rundown is enabled. Volatile m_rundownEnabled; @@ -154,12 +154,16 @@ class EventPipeSessionProvider // The loging level. EventPipeEventLevel m_loggingLevel; + // The filter data. + WCHAR *m_pFilterData; + public: EventPipeSessionProvider( LPCWSTR providerName, UINT64 keywords, - EventPipeEventLevel loggingLevel); + EventPipeEventLevel loggingLevel, + LPCWSTR filterData); ~EventPipeSessionProvider(); LPCWSTR GetProviderName() const; @@ -167,6 +171,8 @@ class EventPipeSessionProvider UINT64 GetKeywords() const; EventPipeEventLevel GetLevel() const; + + LPCWSTR GetFilterData() const; }; #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp index 4ce7c0bd9c35..9ca6add79fd2 100644 --- a/src/vm/eventtrace.cpp +++ b/src/vm/eventtrace.cpp @@ -4244,7 +4244,7 @@ VOID EventPipeEtwCallbackDotNETRuntimeStress( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext) { LIMITED_METHOD_CONTRACT; @@ -4258,7 +4258,7 @@ VOID EventPipeEtwCallbackDotNETRuntime( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext) { LIMITED_METHOD_CONTRACT; @@ -4272,7 +4272,7 @@ VOID EventPipeEtwCallbackDotNETRuntimeRundown( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext) { LIMITED_METHOD_CONTRACT; @@ -4286,7 +4286,7 @@ VOID EventPipeEtwCallbackDotNETRuntimePrivate( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PVOID FilterData, + _In_opt_ EventFilterDescriptor* FilterData, _Inout_opt_ PVOID CallbackContext) { WRAPPER_NO_CONTRACT;