diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 078b4e73ac8044..4a1f2b7225d54e 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -27,3 +27,7 @@ endif(NOT DEFINED FEATURE_STANDALONE_GC) if(NOT DEFINED FEATURE_AUTO_TRACE) set(FEATURE_AUTO_TRACE 0) endif(NOT DEFINED FEATURE_AUTO_TRACE) + +if(NOT DEFINED FEATURE_EVENTPIPE_STARTUP) + set(FEATURE_EVENTPIPE_STARTUP 0) +endif(NOT DEFINED FEATURE_EVENTPIPE_STARTUP) diff --git a/src/coreclr/src/inc/eventtracebase.h b/src/coreclr/src/inc/eventtracebase.h index 2d5b3054c0ab4b..cde996a92185e2 100644 --- a/src/coreclr/src/inc/eventtracebase.h +++ b/src/coreclr/src/inc/eventtracebase.h @@ -234,6 +234,16 @@ extern UINT32 g_nClrInstanceId; #if defined(HOST_UNIX) && (defined(FEATURE_EVENT_TRACE) || defined(FEATURE_EVENTSOURCE_XPLAT)) #define KEYWORDZERO 0x0 +#define DEF_LTTNG_KEYWORD_ENABLED 1 +#ifdef FEATURE_EVENT_TRACE +#include "clrproviders.h" +#endif // FEATURE_EVENT_TRACE +#include "clrconfig.h" + +#endif // defined(HOST_UNIX) && (defined(FEATURE_EVENT_TRACE) || defined(FEATURE_EVENTSOURCE_XPLAT)) + +#if defined(FEATURE_PERFTRACING) + /***************************************/ /* Tracing levels supported by CLR ETW */ /***************************************/ @@ -244,12 +254,6 @@ extern UINT32 g_nClrInstanceId; #define TRACE_LEVEL_INFORMATION 4 // Includes non-error cases such as Entry-Exit #define TRACE_LEVEL_VERBOSE 5 // Detailed traces from intermediate steps -#define DEF_LTTNG_KEYWORD_ENABLED 1 -#ifdef FEATURE_EVENT_TRACE -#include "clrproviders.h" -#endif -#include "clrconfig.h" - class XplatEventLoggerConfiguration { public: @@ -284,6 +288,10 @@ class XplatEventLoggerConfiguration auto levelComponent = GetNextComponentString(keywordsComponent.End + 1); _level = ParseLevel(levelComponent); + + auto argumentComponent = GetNextComponentString(levelComponent.End + 1); + _argument = ParseArgument(argumentComponent); + _isValid = true; } @@ -297,16 +305,21 @@ class XplatEventLoggerConfiguration return _provider; } - ULONGLONG GetEnabledKeywordsMask() const + uint64_t GetEnabledKeywordsMask() const { return _enabledKeywords; } - UINT GetLevel() const + uint32_t GetLevel() const { return _level; } + LPCWSTR GetArgument() const + { + return _argument; + } + private: struct ComponentSpan { @@ -322,9 +335,8 @@ class XplatEventLoggerConfiguration ComponentSpan GetNextComponentString(LPCWSTR start) const { - static WCHAR ComponentDelimiter = W(':'); - - auto end = wcschr(start, ComponentDelimiter); + const WCHAR ComponentDelimiter = W(':'); + const WCHAR * end = wcschr(start, ComponentDelimiter); if (end == nullptr) { end = start + wcslen(start); @@ -333,22 +345,22 @@ class XplatEventLoggerConfiguration return ComponentSpan(start, end); } - LPCWSTR ParseProviderName(ComponentSpan const & component) const + NewArrayHolder ParseProviderName(ComponentSpan const & component) const { - auto providerName = (WCHAR*)nullptr; + NewArrayHolder providerName = nullptr; if ((component.End - component.Start) != 0) { auto const length = component.End - component.Start; providerName = new WCHAR[length + 1]; - memset(providerName, '\0', (length + 1) * sizeof(WCHAR)); wcsncpy(providerName, component.Start, length); + providerName[length] = '\0'; } return providerName; } - ULONGLONG ParseEnabledKeywordsMask(ComponentSpan const & component) const + uint64_t ParseEnabledKeywordsMask(ComponentSpan const & component) const { - auto enabledKeywordsMask = (ULONGLONG)(-1); + auto enabledKeywordsMask = (uint64_t)(-1); if ((component.End - component.Start) != 0) { enabledKeywordsMask = _wcstoui64(component.Start, nullptr, 16); @@ -356,9 +368,9 @@ class XplatEventLoggerConfiguration return enabledKeywordsMask; } - UINT ParseLevel(ComponentSpan const & component) const + uint32_t ParseLevel(ComponentSpan const & component) const { - auto level = TRACE_LEVEL_VERBOSE; + int level = TRACE_LEVEL_VERBOSE; // Verbose if ((component.End - component.Start) != 0) { level = _wtoi(component.Start); @@ -366,11 +378,28 @@ class XplatEventLoggerConfiguration return level; } - LPCWSTR _provider; - ULONGLONG _enabledKeywords; - UINT _level; + NewArrayHolder ParseArgument(ComponentSpan const & component) const + { + NewArrayHolder argument = nullptr; + if ((component.End - component.Start) != 0) + { + auto const length = component.End - component.Start; + argument = new WCHAR[length + 1]; + wcsncpy(argument, component.Start, length); + argument[length] = '\0'; + } + return argument; + } + + NewArrayHolder _provider; + uint64_t _enabledKeywords; + uint32_t _level; + NewArrayHolder _argument; bool _isValid; }; +#endif // FEATURE_PERFTRACING + +#if defined(HOST_UNIX) && (defined(FEATURE_EVENT_TRACE) || defined(FEATURE_EVENTSOURCE_XPLAT)) class XplatEventLoggerController { @@ -497,7 +526,7 @@ class XplatEventLogger } while (configToParse != nullptr) { - static WCHAR comma = W(','); + const WCHAR comma = W(','); auto end = wcschr(configToParse, comma); configuration.Parse(configToParse); XplatEventLoggerController::UpdateProviderContext(configuration); diff --git a/src/coreclr/src/vm/eventpipe.cpp b/src/coreclr/src/vm/eventpipe.cpp index 0c4581fe523e03..06d0ed7ac2c90e 100644 --- a/src/coreclr/src/vm/eventpipe.cpp +++ b/src/coreclr/src/vm/eventpipe.cpp @@ -21,6 +21,7 @@ #include "sampleprofiler.h" #include "win32threadpool.h" #include "ceemain.h" +#include "configuration.h" #ifdef TARGET_UNIX #include "pal.h" @@ -94,12 +95,118 @@ void EventPipe::Initialize() #endif } - { CrstHolder _crst(GetLock()); if (tracingInitialized) s_state = EventPipeState::Initialized; } +#ifdef FEATURE_EVENTPIPE_STARTUP + EnableViaEnvironmentVariables(); +#endif // FEATURE_EVENTPIPE_STARTUP +} + +// +// If EventPipe environment variables are specified, parse them and start a session +// +void EventPipe::EnableViaEnvironmentVariables() +{ + STANDARD_VM_CONTRACT; + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableEventPipe) != 0) + { + CLRConfigStringHolder eventpipeConfig(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeConfig)); + CLRConfigStringHolder configOutputPath(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeOutputPath)); + uint32_t eventpipeCircularBufferMB = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeCircularMB); + LPCWSTR outputPath = nullptr; + + if (configOutputPath == NULL) + { + outputPath = W("trace.nettrace"); + } + else + { + outputPath = configOutputPath; + } + auto configuration = XplatEventLoggerConfiguration(); + LPWSTR configToParse = eventpipeConfig; + int providerCnt = 0; + + // Create EventPipeProviderConfiguration and start tracing. + NewHolder pProviders = nullptr; + + // If COMPlus_EnableEventPipe is set to 1 but no configuration was specified, enable EventPipe session + // with the default provider configurations. + if (configToParse == nullptr || *configToParse == L'\0') + { + providerCnt = 2; + pProviders = new EventPipeProviderConfiguration[providerCnt]; + pProviders[0] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntime"), 0x4c14fccbd, 5, nullptr); + pProviders[1] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntimePrivate"), 0x4002000b, 5, nullptr); + } + else + { + // Count how many providers there are to parse + static WCHAR comma = W(','); + while (*configToParse != '\0') + { + providerCnt += 1; + auto end = wcschr(configToParse, comma); + if (end == nullptr) + { + break; + } + configToParse = end + 1; + } + configToParse = eventpipeConfig; + pProviders = new EventPipeProviderConfiguration[providerCnt]; + int i = 0; + while (*configToParse != '\0') + { + auto end = wcschr(configToParse, comma); + configuration.Parse(configToParse); + + // if we find any invalid configuration, do not trace. + if (!configuration.IsValid()) + { + return; + } + // SampleProfiler can't be enabled on startup yet. + else if (wcscmp(W("Microsoft-DotNETCore-SampleProfiler"), configuration.GetProviderName()) == 0) + { + providerCnt -= 1; + } + else + { + pProviders[i++] = EventPipeProviderConfiguration( + configuration.GetProviderName(), + configuration.GetEnabledKeywordsMask(), + configuration.GetLevel(), + configuration.GetArgument() + ); + } + + if (end == nullptr) + { + break; + } + configToParse = end + 1; + } + } + + if (providerCnt != 0) + { + uint64_t sessionID = EventPipe::Enable( + outputPath, + eventpipeCircularBufferMB, + pProviders, + providerCnt, + EventPipeSessionType::File, + EventPipeSerializationFormat::NetTraceV4, + true, + nullptr + ); + EventPipe::StartStreaming(sessionID); + } + } } void EventPipe::Shutdown() diff --git a/src/coreclr/src/vm/eventpipe.h b/src/coreclr/src/vm/eventpipe.h index aa1c10cfd94454..a638ca118cd065 100644 --- a/src/coreclr/src/vm/eventpipe.h +++ b/src/coreclr/src/vm/eventpipe.h @@ -47,6 +47,9 @@ class EventPipe // Initialize the event pipe. static void Initialize(); + // Initialize environment variable based session + static void EnableViaEnvironmentVariables(); + // Shutdown the event pipe. static void Shutdown();