diff options
-rw-r--r-- | src/inc/clrconfigvalues.h | 10 | ||||
-rw-r--r-- | src/vm/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/vm/eventpipe.cpp | 192 | ||||
-rw-r--r-- | src/vm/eventpipe.h | 9 | ||||
-rw-r--r-- | src/vm/eventpipeconfiguration.cpp | 298 | ||||
-rw-r--r-- | src/vm/eventpipeconfiguration.h | 95 | ||||
-rw-r--r-- | src/vm/eventpipeprovider.cpp | 1 | ||||
-rw-r--r-- | src/vm/eventpipesession.cpp | 296 | ||||
-rw-r--r-- | src/vm/eventpipesession.h | 135 |
9 files changed, 730 insertions, 307 deletions
diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h index 95179d8ed2..afb67986ff 100644 --- a/src/inc/clrconfigvalues.h +++ b/src/inc/clrconfigvalues.h @@ -728,10 +728,12 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableRCWCleanupOnSTAShutdown, W("EnableRCWCle RETAIL_CONFIG_STRING_INFO(INTERNAL_LocalWinMDPath, W("LocalWinMDPath"), "Additional path to probe for WinMD files in if a WinRT type is not resolved using the standard paths.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_AllowDComReflection, W("AllowDComReflection"), 0, "Allows out of process DCOM clients to marshal blocked reflection types.") -/// -/// Performance Tracing -/// -RETAIL_CONFIG_DWORD_INFO(INTERNAL_PerformanceTracing, W("PerformanceTracing"), 0, "Enable/disable performance tracing. Non-zero values enable tracing.") +// +// EventPipe +// +RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableEventPipe, W("EnableEventPipe"), 0, "Enable/disable event pipe. Non-zero values enable tracing.") +RETAIL_CONFIG_STRING_INFO(INTERNAL_EventPipeOutputFile, W("EventPipeOutputFile"), "The full path including file name for the trace file that will be written when COMPlus_EnableEventPipe&=1") +RETAIL_CONFIG_STRING_INFO(INTERNAL_EventPipeConfig, W("EventPipeConfig"), "Configuration for EventPipe.") #ifdef FEATURE_GDBJIT /// diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 67aece1c8a..5a30bce6b8 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -189,6 +189,7 @@ set(VM_SOURCES_WKS eventpipeprovider.cpp eventpipebuffer.cpp eventpipebuffermanager.cpp + eventpipesession.cpp eventstore.cpp fastserializer.cpp fcall.cpp diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index 37de4c3b36..2eeb64f4b2 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -11,6 +11,7 @@ #include "eventpipeevent.h" #include "eventpipefile.h" #include "eventpipeprovider.h" +#include "eventpipesession.h" #include "eventpipejsonfile.h" #include "sampleprofiler.h" @@ -23,6 +24,7 @@ CrstStatic EventPipe::s_configCrst; bool EventPipe::s_tracingInitialized = false; EventPipeConfiguration* EventPipe::s_pConfig = NULL; +EventPipeSession* EventPipe::s_pSession = NULL; EventPipeBufferManager* EventPipe::s_pBufferManager = NULL; EventPipeFile* EventPipe::s_pFile = NULL; #ifdef _DEBUG @@ -206,15 +208,22 @@ void EventPipe::EnableOnStartup() CONTRACTL_END; // Test COMPLUS variable to enable tracing at start-up. - if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 1) == 1) + if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableEventPipe) & 1) == 1) { SString outputPath; outputPath.Printf("Process-%d.netperf", GetCurrentProcessId()); - Enable( - outputPath.GetUnicode(), + + // Create a new session. + EventPipeSession *pSession = new EventPipeSession( 1024 /* 1 GB circular buffer */, - NULL /* pProviders */, + NULL, /* pProviders */ 0 /* numProviders */); + + // Get the configuration from the environment. + GetConfigurationFromEnvironment(outputPath, pSession); + + // Enable the session. + Enable(outputPath, pSession); } } @@ -262,14 +271,32 @@ void EventPipe::Enable( } CONTRACTL_END; + // Create a new session. + EventPipeSession *pSession = s_pConfig->CreateSession(circularBufferSizeInMB, pProviders, static_cast<unsigned int>(numProviders)); + + // Enable the session. + Enable(strOutputPath, pSession); +} + +void EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(pSession != NULL); + } + CONTRACTL_END; + // If tracing is not initialized or is already enabled, bail here. if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) { return; } - // If the state or aurguments are invalid, bail - if(pProviders == NULL || numProviders <= 0) + // If the state or arguments are invalid, bail here. + if(pSession == NULL || !pSession->IsValid()) { return; } @@ -282,7 +309,7 @@ void EventPipe::Enable( s_pFile = new EventPipeFile(eventPipeFileOutputPath); #ifdef _DEBUG - if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 2) == 2) + if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableEventPipe) & 2) == 2) { // Create a synchronous file. SString eventPipeSyncFileOutputPath; @@ -296,8 +323,11 @@ void EventPipe::Enable( } #endif // _DEBUG + // Save the session. + s_pSession = pSession; + // Enable tracing. - s_pConfig->Enable(circularBufferSizeInMB, pProviders, numProviders); + s_pConfig->Enable(s_pSession); // Enable the sample profiler SampleProfiler::Enable(); @@ -325,7 +355,11 @@ void EventPipe::Disable() SampleProfiler::Disable(); // Disable tracing. - s_pConfig->Disable(); + s_pConfig->Disable(s_pSession); + + // Delete the session. + s_pConfig->DeleteSession(s_pSession); + s_pSession = NULL; // Flush all write buffers to make sure that all threads see the change. FlushProcessWriteBuffers(); @@ -336,7 +370,15 @@ void EventPipe::Disable() s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp); // Before closing the file, do rundown. - s_pConfig->EnableRundown(); + const unsigned int numRundownProviders = 2; + EventPipeProviderConfiguration rundownProviders[] = + { + { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose) }, // Public provider. + { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose) } // Rundown provider. + }; + // The circular buffer size doesn't matter because all events are written synchronously during rundown. + s_pSession = s_pConfig->CreateSession(1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders); + s_pConfig->EnableRundown(s_pSession); // Ask the runtime to emit rundown events. if(g_fEEStarted && !g_fEEShutDown) @@ -345,7 +387,11 @@ void EventPipe::Disable() } // Disable the event pipe now that rundown is complete. - s_pConfig->Disable(); + s_pConfig->Disable(s_pSession); + + // Delete the rundown session. + s_pConfig->DeleteSession(s_pSession); + s_pSession = NULL; if(s_pFile != NULL) { @@ -710,6 +756,130 @@ CrstStatic* EventPipe::GetLock() return &s_configCrst; } +void EventPipe::GetConfigurationFromEnvironment(SString &outputPath, EventPipeSession *pSession) +{ + LIMITED_METHOD_CONTRACT; + + // Set the output path if specified. + CLRConfigStringHolder wszOutputPath(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeOutputFile)); + if(wszOutputPath != NULL) + { + outputPath.Set(wszOutputPath); + } + + // Read the the provider configuration from the environment if specified. + CLRConfigStringHolder wszConfig(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeConfig)); + if(wszConfig == NULL) + { + pSession->EnableAllEvents(); + return; + } + + size_t len = wcslen(wszConfig); + if(len <= 0) + { + pSession->EnableAllEvents(); + return; + } + + // Parses a string with the following format: + // + // ProviderName:Keywords:Level[,]* + // + // For example: + // + // Microsoft-Windows-DotNETRuntime:0xCAFEBABE:2,Microsoft-Windows-DotNETRuntimePrivate:0xDEADBEEF:1 + // + // Each provider configuration is separated by a ',' and each component within the configuration is + // separated by a ':'. + + const WCHAR ProviderSeparatorChar = ','; + const WCHAR ComponentSeparatorChar = ':'; + size_t index = 0; + WCHAR *pProviderName = NULL; + UINT64 keywords = 0; + EventPipeEventLevel level = EventPipeEventLevel::Critical; + + while(index < len) + { + WCHAR * pCurrentChunk = &wszConfig[index]; + size_t currentChunkStartIndex = index; + size_t currentChunkEndIndex = 0; + + // Find the next chunk. + while(index < len && wszConfig[index] != ProviderSeparatorChar) + { + index++; + } + currentChunkEndIndex = index++; + + // Split the chunk into components. + size_t chunkIndex = currentChunkStartIndex; + + // Get the provider name. + size_t provNameStartIndex = chunkIndex; + size_t provNameEndIndex = currentChunkEndIndex; + + while(chunkIndex < currentChunkEndIndex && wszConfig[chunkIndex] != ComponentSeparatorChar) + { + chunkIndex++; + } + provNameEndIndex = chunkIndex++; + + size_t provNameLen = provNameEndIndex - provNameStartIndex; + pProviderName = new WCHAR[provNameLen+1]; + memcpy(pProviderName, &wszConfig[provNameStartIndex], provNameLen*sizeof(WCHAR)); + pProviderName[provNameLen] = '\0'; + + // Get the keywords. + size_t keywordsStartIndex = chunkIndex; + size_t keywordsEndIndex = currentChunkEndIndex; + + while(chunkIndex < currentChunkEndIndex && wszConfig[chunkIndex] != ComponentSeparatorChar) + { + chunkIndex++; + } + keywordsEndIndex = chunkIndex++; + + size_t keywordsLen = keywordsEndIndex - keywordsStartIndex; + WCHAR *wszKeywords = new WCHAR[keywordsLen+1]; + memcpy(wszKeywords, &wszConfig[keywordsStartIndex], keywordsLen*sizeof(WCHAR)); + wszKeywords[keywordsLen] = '\0'; + keywords = _wcstoui64(wszKeywords, NULL, 16); + delete[] wszKeywords; + wszKeywords = NULL; + + // Get the level. + size_t levelStartIndex = chunkIndex; + size_t levelEndIndex = currentChunkEndIndex; + + while(chunkIndex < currentChunkEndIndex && wszConfig[chunkIndex] != ComponentSeparatorChar) + { + chunkIndex++; + } + levelEndIndex = chunkIndex++; + + size_t levelLen = levelEndIndex - levelStartIndex; + WCHAR *wszLevel = new WCHAR[levelLen+1]; + memcpy(wszLevel, &wszConfig[levelStartIndex], levelLen*sizeof(WCHAR)); + wszLevel[levelLen] = '\0'; + level = (EventPipeEventLevel) wcstoul(wszLevel, NULL, 16); + delete[] wszLevel; + wszLevel = NULL; + + // Add a new EventPipeSessionProvider. + EventPipeSessionProvider *pSessionProvider = new EventPipeSessionProvider(pProviderName, keywords, level); + pSession->AddSessionProvider(pSessionProvider); + + // Free the provider name string. + if(pProviderName != NULL) + { + delete[] pProviderName; + pProviderName = NULL; + } + } +} + void QCALLTYPE EventPipeInternal::Enable( __in_z LPCWSTR outputFile, UINT32 circularBufferSizeInMB, diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index 4fd3d88127..d1f7d60f8e 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -20,6 +20,7 @@ class EventPipeProvider; class MethodDesc; class SampleProfilerEventInstance; struct EventPipeProviderConfiguration; +class EventPipeSession; // Define the event pipe callback to match the ETW callback signature. typedef void (*EventPipeCallback)( @@ -275,6 +276,13 @@ class EventPipe private: + // Enable the specified EventPipe session. + static void Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); + + // Get the EnableOnStartup configuration from environment. + static void GetConfigurationFromEnvironment(SString &outputPath, EventPipeSession *pSession); + + // Callback function for the stack walker. For each frame walked, this callback is invoked. static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData); @@ -288,6 +296,7 @@ class EventPipe static CrstStatic s_configCrst; static bool s_tracingInitialized; static EventPipeConfiguration *s_pConfig; + static EventPipeSession *s_pSession; static EventPipeBufferManager *s_pBufferManager; static EventPipeFile *s_pFile; #ifdef _DEBUG diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp index a74bdbc28f..4678fc36fb 100644 --- a/src/vm/eventpipeconfiguration.cpp +++ b/src/vm/eventpipeconfiguration.cpp @@ -7,6 +7,7 @@ #include "eventpipeconfiguration.h" #include "eventpipeeventinstance.h" #include "eventpipeprovider.h" +#include "eventpipesession.h" #ifdef FEATURE_PERFTRACING @@ -18,9 +19,8 @@ EventPipeConfiguration::EventPipeConfiguration() m_enabled = false; m_rundownEnabled = false; - m_circularBufferSizeInBytes = 1024 * 1024 * 1000; // Default to 1000MB. - m_pEnabledProviderList = NULL; m_pConfigProvider = NULL; + m_pSession = NULL; m_pProviderList = new SList<SListElem<EventPipeProvider*>>(); } @@ -46,11 +46,10 @@ EventPipeConfiguration::~EventPipeConfiguration() EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); } - - if(m_pEnabledProviderList != NULL) + if(m_pSession != NULL) { - delete(m_pEnabledProviderList); - m_pEnabledProviderList = NULL; + DeleteSession(m_pSession); + m_pSession = NULL; } if(m_pProviderList != NULL) @@ -173,16 +172,16 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider) m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider)); } - // Set the provider configuration and enable it if we know anything about the provider before it is registered. - if(m_pEnabledProviderList != NULL) + // Set the provider configuration and enable it if it has been requested by a session. + if(m_pSession != NULL) { - EventPipeEnabledProvider *pEnabledProvider = m_pEnabledProviderList->GetEnabledProvider(&provider); - if(pEnabledProvider != NULL) + EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, &provider); + if(pSessionProvider != NULL) { provider.SetConfiguration( true /* providerEnabled */, - pEnabledProvider->GetKeywords(), - pEnabledProvider->GetLevel()); + pSessionProvider->GetKeywords(), + pSessionProvider->GetLevel()); } } @@ -278,40 +277,83 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov return NULL; } +EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); + } + CONTRACTL_END; + + EventPipeSessionProvider *pRet = NULL; + if(pSession != NULL) + { + pRet = pSession->GetSessionProvider(pProvider); + } + return pRet; +} + size_t EventPipeConfiguration::GetCircularBufferSize() const { LIMITED_METHOD_CONTRACT; - return m_circularBufferSizeInBytes; + size_t ret = 0; + if(m_pSession != NULL) + { + ret = m_pSession->GetCircularBufferSize(); + } + return ret; } -void EventPipeConfiguration::SetCircularBufferSize(size_t circularBufferSize) +EventPipeSession* EventPipeConfiguration::CreateSession(unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + return new EventPipeSession(circularBufferSizeInMB, pProviders, numProviders); +} + +void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(pSession != NULL); + PRECONDITION(m_enabled == false); + } + CONTRACTL_END; - if(!m_enabled) + // TODO: Multiple session support will require individual enabled bits. + if(pSession != NULL && !m_enabled) { - m_circularBufferSizeInBytes = circularBufferSize; + delete(pSession); } } -void EventPipeConfiguration::Enable( - unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - int numProviders) +void EventPipeConfiguration::Enable(EventPipeSession *pSession) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; + PRECONDITION(pSession != NULL); // Lock must be held by EventPipe::Enable. PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); } CONTRACTL_END; - m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; - m_pEnabledProviderList = new EventPipeEnabledProviderList(pProviders, static_cast<unsigned int>(numProviders)); + m_pSession = pSession; m_enabled = true; // The provider list should be non-NULL, but can be NULL on shutdown. @@ -323,13 +365,13 @@ void EventPipeConfiguration::Enable( EventPipeProvider *pProvider = pElem->GetValue(); // Enable the provider if it has been configured. - EventPipeEnabledProvider *pEnabledProvider = m_pEnabledProviderList->GetEnabledProvider(pProvider); - if(pEnabledProvider != NULL) + EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, pProvider); + if(pSessionProvider != NULL) { pProvider->SetConfiguration( true /* providerEnabled */, - pEnabledProvider->GetKeywords(), - pEnabledProvider->GetLevel()); + pSessionProvider->GetKeywords(), + pSessionProvider->GetLevel()); } pElem = m_pProviderList->GetNext(pElem); @@ -337,13 +379,16 @@ void EventPipeConfiguration::Enable( } } -void EventPipeConfiguration::Disable() +void EventPipeConfiguration::Disable(EventPipeSession *pSession) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; + // TODO: Multiple session support will require that the session be specified. + PRECONDITION(pSession != NULL); + PRECONDITION(pSession == m_pSession); // Lock must be held by EventPipe::Disable. PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); } @@ -364,13 +409,7 @@ void EventPipeConfiguration::Disable() m_enabled = false; m_rundownEnabled = false; - - // Free the enabled providers list. - if(m_pEnabledProviderList != NULL) - { - delete(m_pEnabledProviderList); - m_pEnabledProviderList = NULL; - } + m_pSession = NULL; } bool EventPipeConfiguration::Enabled() const @@ -385,30 +424,28 @@ bool EventPipeConfiguration::RundownEnabled() const return m_rundownEnabled; } -void EventPipeConfiguration::EnableRundown() +void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; + PRECONDITION(pSession != NULL); // Lock must be held by EventPipe::Disable. PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); } CONTRACTL_END; // Build the rundown configuration. - _ASSERTE(m_pEnabledProviderList == NULL); - const unsigned int numRundownProviders = 2; - EventPipeProviderConfiguration rundownProviders[numRundownProviders]; - rundownProviders[0] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Public provider. - rundownProviders[1] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Rundown provider. + _ASSERTE(m_pSession == NULL); // Enable rundown. + // TODO: Move this into EventPipeSession once Enable takes an EventPipeSession object. m_rundownEnabled = true; - // Enable tracing. The circular buffer size doesn't matter because we're going to write all events synchronously during rundown. - Enable(1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders); + // Enable tracing. + Enable(pSession); } EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance) @@ -506,179 +543,4 @@ void EventPipeConfiguration::DeleteDeferredProviders() } } } - -EventPipeEnabledProviderList::EventPipeEnabledProviderList( - EventPipeProviderConfiguration *pConfigs, - unsigned int numConfigs) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_pProviders = NULL; - m_pCatchAllProvider = NULL; - m_numProviders = 0; - - // Test COMPLUS variable to enable tracing at start-up. - // If tracing is enabled at start-up create the catch-all provider and always return it. - if((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) & 1) == 1) - { - m_pCatchAllProvider = new EventPipeEnabledProvider(); - m_pCatchAllProvider->Set(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose); - return; - } - - m_pCatchAllProvider = NULL; - m_numProviders = numConfigs; - if(m_numProviders == 0) - { - return; - } - - m_pProviders = new EventPipeEnabledProvider[m_numProviders]; - for(unsigned int i=0; i<m_numProviders; i++) - { - m_pProviders[i].Set( - pConfigs[i].GetProviderName(), - pConfigs[i].GetKeywords(), - (EventPipeEventLevel)pConfigs[i].GetLevel()); - } -} - -EventPipeEnabledProviderList::~EventPipeEnabledProviderList() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pProviders != NULL) - { - delete[] m_pProviders; - m_pProviders = NULL; - } - if(m_pCatchAllProvider != NULL) - { - delete(m_pCatchAllProvider); - m_pCatchAllProvider = NULL; - } -} - -EventPipeEnabledProvider* EventPipeEnabledProviderList::GetEnabledProvider( - EventPipeProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // If tracing was enabled on start-up, all events should be on (this is a diagnostic config). - if(m_pCatchAllProvider != NULL) - { - return m_pCatchAllProvider; - } - - if(m_pProviders == NULL) - { - return NULL; - } - - SString providerNameStr = pProvider->GetProviderName(); - LPCWSTR providerName = providerNameStr.GetUnicode(); - - EventPipeEnabledProvider *pEnabledProvider = NULL; - for(unsigned int i=0; i<m_numProviders; i++) - { - EventPipeEnabledProvider *pCandidate = &m_pProviders[i]; - if(pCandidate != NULL) - { - if(wcscmp(providerName, pCandidate->GetProviderName()) == 0) - { - pEnabledProvider = pCandidate; - break; - } - } - } - - return pEnabledProvider; -} - -EventPipeEnabledProvider::EventPipeEnabledProvider() -{ - LIMITED_METHOD_CONTRACT; - m_pProviderName = NULL; - m_keywords = 0; -} - -EventPipeEnabledProvider::~EventPipeEnabledProvider() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pProviderName != NULL) - { - delete[] m_pProviderName; - m_pProviderName = NULL; - } -} - -void EventPipeEnabledProvider::Set(LPCWSTR providerName, UINT64 keywords, EventPipeEventLevel loggingLevel) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pProviderName != NULL) - { - delete(m_pProviderName); - m_pProviderName = NULL; - } - - if(providerName != NULL) - { - size_t bufSize = wcslen(providerName) + 1; - m_pProviderName = new WCHAR[bufSize]; - wcscpy_s(m_pProviderName, bufSize, providerName); - } - m_keywords = keywords; - m_loggingLevel = loggingLevel; -} - -LPCWSTR EventPipeEnabledProvider::GetProviderName() const -{ - LIMITED_METHOD_CONTRACT; - return m_pProviderName; -} - -UINT64 EventPipeEnabledProvider::GetKeywords() const -{ - LIMITED_METHOD_CONTRACT; - return m_keywords; -} - -EventPipeEventLevel EventPipeEnabledProvider::GetLevel() const -{ - LIMITED_METHOD_CONTRACT; - return m_loggingLevel; -} - #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h index baca06920a..d1a8a90c46 100644 --- a/src/vm/eventpipeconfiguration.h +++ b/src/vm/eventpipeconfiguration.h @@ -8,12 +8,13 @@ #include "slist.h" -class EventPipeEnabledProvider; -class EventPipeEnabledProviderList; +class EventPipeSessionProvider; class EventPipeEvent; class EventPipeEventInstance; class EventPipeProvider; struct EventPipeProviderConfiguration; +class EventPipeSession; +class EventPipeSessionProvider; enum class EventPipeEventLevel { @@ -50,20 +51,20 @@ public: // Get the provider with the specified provider ID if it exists. EventPipeProvider* GetProvider(const SString &providerID); + // Create a new session. + EventPipeSession* CreateSession(unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders); + + // Delete a session. + void DeleteSession(EventPipeSession *pSession); + // Get the configured size of the circular buffer. size_t GetCircularBufferSize() const; - // Set the configured size of the circular buffer. - void SetCircularBufferSize(size_t circularBufferSize); - - // Enable the event pipe. - void Enable( - unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - int numProviders); + // Enable a session in the event pipe. + void Enable(EventPipeSession *pSession); - // Disable the event pipe. - void Disable(); + // Disable a session in the event pipe. + void Disable(EventPipeSession *pSession); // Get the status of the event pipe. bool Enabled() const; @@ -71,8 +72,8 @@ public: // Determine if rundown is enabled. bool RundownEnabled() const; - // Enable the well-defined symbolic rundown configuration. - void EnableRundown(); + // Enable rundown using the specified configuration. + void EnableRundown(EventPipeSession *pSession); // Get the event used to write metadata to the event stream. EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance); @@ -85,15 +86,14 @@ private: // Get the provider without taking the lock. EventPipeProvider* GetProviderNoLock(const SString &providerID); - // Determines whether or not the event pipe is enabled. - Volatile<bool> m_enabled; + // Get the enabled provider. + EventPipeSessionProvider* GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider); - // The configured size of the circular buffer. - size_t m_circularBufferSizeInBytes; + // The one and only EventPipe session. + EventPipeSession *m_pSession; - // EventPipeConfiguration only supports a single session. - // This is the set of configurations for each enabled provider. - EventPipeEnabledProviderList *m_pEnabledProviderList; + // Determines whether or not the event pipe is enabled. + Volatile<bool> m_enabled; // The list of event pipe providers. SList<SListElem<EventPipeProvider*>> *m_pProviderList; @@ -112,59 +112,6 @@ private: Volatile<bool> m_rundownEnabled; }; -class EventPipeEnabledProviderList -{ - -private: - - // The number of providers in the list. - unsigned int m_numProviders; - - // The list of providers. - EventPipeEnabledProvider *m_pProviders; - - // A catch-all provider used when tracing is enabled at start-up - // under (COMPlus_PerformanceTracing & 1) == 1. - EventPipeEnabledProvider *m_pCatchAllProvider; - -public: - - // Create a new list based on the input. - EventPipeEnabledProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int numConfigs); - ~EventPipeEnabledProviderList(); - - // Get the enabled provider for the specified provider. - // Return NULL if one doesn't exist. - EventPipeEnabledProvider* GetEnabledProvider(EventPipeProvider *pProvider); -}; - -class EventPipeEnabledProvider -{ -private: - - // The provider name. - WCHAR *m_pProviderName; - - // The enabled keywords. - UINT64 m_keywords; - - // The loging level. - EventPipeEventLevel m_loggingLevel; - -public: - - EventPipeEnabledProvider(); - ~EventPipeEnabledProvider(); - - void Set(LPCWSTR providerName, UINT64 keywords, EventPipeEventLevel loggingLevel); - - LPCWSTR GetProviderName() const; - - UINT64 GetKeywords() const; - - EventPipeEventLevel GetLevel() const; -}; - #endif // FEATURE_PERFTRACING #endif // __EVENTPIPE_CONFIGURATION_H__ diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp index 84a90e4abb..3e82706801 100644 --- a/src/vm/eventpipeprovider.cpp +++ b/src/vm/eventpipeprovider.cpp @@ -31,6 +31,7 @@ EventPipeProvider::EventPipeProvider(EventPipeConfiguration *pConfig, const SStr m_pCallbackFunction = pCallbackFunction; m_pCallbackData = pCallbackData; m_pConfig = pConfig; + m_deleteDeferred = false; } EventPipeProvider::~EventPipeProvider() diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp new file mode 100644 index 0000000000..7fd7ac11cc --- /dev/null +++ b/src/vm/eventpipesession.cpp @@ -0,0 +1,296 @@ +// 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. + +#include "common.h" +#include "eventpipe.h" +#include "eventpipeprovider.h" +#include "eventpipesession.h" + +#ifdef FEATURE_PERFTRACING + +EventPipeSession::EventPipeSession( + unsigned int circularBufferSizeInMB, + EventPipeProviderConfiguration *pProviders, + unsigned int numProviders) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB; + m_rundownEnabled = false; + m_pProviderList = new EventPipeSessionProviderList( + pProviders, + numProviders); +} + +EventPipeSession::~EventPipeSession() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if(m_pProviderList != NULL) + { + delete m_pProviderList; + m_pProviderList = NULL; + } +} + +bool EventPipeSession::IsValid() const +{ + LIMITED_METHOD_CONTRACT; + + if((m_pProviderList == NULL) || (m_pProviderList->IsEmpty())) + { + return false; + } + + return true; +} + +void EventPipeSession::AddSessionProvider(EventPipeSessionProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + m_pProviderList->AddSessionProvider(pProvider); +} + +void EventPipeSession::EnableAllEvents() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + m_pProviderList->EnableAllEvents(); +} + +EventPipeSessionProvider* EventPipeSession::GetSessionProvider(EventPipeProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + return m_pProviderList->GetSessionProvider(pProvider); +} + +EventPipeSessionProviderList::EventPipeSessionProviderList( + EventPipeProviderConfiguration *pConfigs, + unsigned int numConfigs) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + m_pProviders = new SList<SListElem<EventPipeSessionProvider*>>(); + m_pCatchAllProvider = NULL; + for(unsigned int i=0; i<numConfigs; i++) + { + EventPipeProviderConfiguration *pConfig = &pConfigs[i]; + EventPipeSessionProvider *pProvider = new EventPipeSessionProvider( + pConfig->GetProviderName(), + pConfig->GetKeywords(), + (EventPipeEventLevel)pConfig->GetLevel()); + + m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider)); + } +} + +EventPipeSessionProviderList::~EventPipeSessionProviderList() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if(m_pProviders != NULL) + { + SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead(); + while(pElem != NULL) + { + EventPipeSessionProvider *pProvider = pElem->GetValue(); + delete pProvider; + + SListElem<EventPipeSessionProvider*> *pCurElem = pElem; + pElem = m_pProviders->GetNext(pElem); + delete pCurElem; + } + + delete m_pProviders; + m_pProviders = NULL; + } + if(m_pCatchAllProvider != NULL) + { + delete(m_pCatchAllProvider); + m_pCatchAllProvider = NULL; + } +} + +void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if(pProvider != NULL) + { + m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider)); + } +} + +void EventPipeSessionProviderList::EnableAllEvents() +{ + LIMITED_METHOD_CONTRACT; + + if(m_pCatchAllProvider == NULL) + { + m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose); + } +} + +EventPipeSessionProvider* EventPipeSessionProviderList::GetSessionProvider( + EventPipeProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config. + if(m_pCatchAllProvider != NULL) + { + return m_pCatchAllProvider; + } + + if(m_pProviders == NULL) + { + return NULL; + } + + SString providerNameStr = pProvider->GetProviderName(); + LPCWSTR providerName = providerNameStr.GetUnicode(); + + EventPipeSessionProvider *pSessionProvider = NULL; + SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead(); + while(pElem != NULL) + { + EventPipeSessionProvider *pCandidate = pElem->GetValue(); + if(wcscmp(providerName, pCandidate->GetProviderName()) == 0) + { + pSessionProvider = pCandidate; + break; + } + pElem = m_pProviders->GetNext(pElem); + } + + return pSessionProvider; +} + +bool EventPipeSessionProviderList::IsEmpty() const +{ + LIMITED_METHOD_CONTRACT; + + return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL); +} + +EventPipeSessionProvider::EventPipeSessionProvider( + LPCWSTR providerName, + UINT64 keywords, + EventPipeEventLevel loggingLevel) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if(providerName != NULL) + { + size_t bufSize = wcslen(providerName) + 1; + m_pProviderName = new WCHAR[bufSize]; + wcscpy_s(m_pProviderName, bufSize, providerName); + } + else + { + m_pProviderName = NULL; + } + m_keywords = keywords; + m_loggingLevel = loggingLevel; + +} + +EventPipeSessionProvider::~EventPipeSessionProvider() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if(m_pProviderName != NULL) + { + delete[] m_pProviderName; + m_pProviderName = NULL; + } +} + +LPCWSTR EventPipeSessionProvider::GetProviderName() const +{ + LIMITED_METHOD_CONTRACT; + return m_pProviderName; +} + +UINT64 EventPipeSessionProvider::GetKeywords() const +{ + LIMITED_METHOD_CONTRACT; + return m_keywords; +} + +EventPipeEventLevel EventPipeSessionProvider::GetLevel() const +{ + LIMITED_METHOD_CONTRACT; + return m_loggingLevel; +} + +#endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h new file mode 100644 index 0000000000..ba91c60aaa --- /dev/null +++ b/src/vm/eventpipesession.h @@ -0,0 +1,135 @@ +// 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. + +#ifndef __EVENTPIPE_SESSION_H__ +#define __EVENTPIPE_SESSION_H__ + +#ifdef FEATURE_PERFTRACING + +enum class EventPipeEventLevel; +struct EventPipeProviderConfiguration; +class EventPipeSessionProviderList; +class EventPipeSessionProvider; + +class EventPipeSession +{ +private: + // The set of configurations for each provider in the session. + EventPipeSessionProviderList *m_pProviderList; + + // The configured size of the circular buffer. + size_t m_circularBufferSizeInBytes; + + // True if rundown is enabled. + Volatile<bool> m_rundownEnabled; + +public: + + // TODO: This needs to be exposed via EventPipe::CreateSession() and EventPipe::DeleteSession() to avoid memory ownership issues. + EventPipeSession( + unsigned int circularBufferSizeInMB, + EventPipeProviderConfiguration *pProviders, + unsigned int numProviders); + + ~EventPipeSession(); + + // Determine if the session is valid or not. Invalid sessions can be detected before they are enabled. + bool IsValid() const; + + // Get the configured size of the circular buffer. + size_t GetCircularBufferSize() const + { + LIMITED_METHOD_CONTRACT; + return m_circularBufferSizeInBytes; + } + + // Determine if rundown is enabled. + bool RundownEnabled() const + { + LIMITED_METHOD_CONTRACT; + return m_rundownEnabled; + } + + // Set the rundown enabled flag. + void SetRundownEnabled(bool value) + { + LIMITED_METHOD_CONTRACT; + m_rundownEnabled = value; + } + + // Enable all events. + // This is used for testing and is controlled via COMPLUS_EnableEventPipe. + void EnableAllEvents(); + + // Add a new provider to the session. + void AddSessionProvider(EventPipeSessionProvider *pProvider); + + // Get the session provider for the specified provider if present. + EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider); +}; + +class EventPipeSessionProviderList +{ + +private: + + // The list of providers. + SList<SListElem<EventPipeSessionProvider*>> *m_pProviders; + + // A catch-all provider used when tracing is enabled at start-up + // under (COMPlus_PerformanceTracing & 1) == 1. + EventPipeSessionProvider *m_pCatchAllProvider; + +public: + + // Create a new list based on the input. + EventPipeSessionProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int numConfigs); + ~EventPipeSessionProviderList(); + + // Enable all events. + // This is used for testing and is controlled via COMPLUS_EnableEventPipe. + void EnableAllEvents(); + + // Add a new session provider to the list. + void AddSessionProvider(EventPipeSessionProvider *pProvider); + + // Get the session provider for the specified provider. + // Return NULL if one doesn't exist. + EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider); + + // Returns true if the list is empty. + bool IsEmpty() const; +}; + +class EventPipeSessionProvider +{ +private: + + // The provider name. + WCHAR *m_pProviderName; + + // The enabled keywords. + UINT64 m_keywords; + + // The loging level. + EventPipeEventLevel m_loggingLevel; + +public: + + EventPipeSessionProvider( + LPCWSTR providerName, + UINT64 keywords, + EventPipeEventLevel loggingLevel); + ~EventPipeSessionProvider(); + + LPCWSTR GetProviderName() const; + + UINT64 GetKeywords() const; + + EventPipeEventLevel GetLevel() const; +}; + +#endif // FEATURE_PERFTRACING + +#endif // __EVENTPIPE_SESSION_H__ |