diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 674246d8e0216..d258094a79da9 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -104,9 +104,7 @@ add_definitions(-DFEATURE_DEFAULT_INTERFACES) if(FEATURE_EVENT_TRACE) add_compile_definitions($<$>>:FEATURE_EVENT_TRACE>) add_definitions(-DFEATURE_PERFTRACING) - if(FEATURE_PERFTRACING_C_LIB) - add_definitions(-DFEATURE_PERFTRACING_C_LIB) - endif(FEATURE_PERFTRACING_C_LIB) + add_definitions(-DFEATURE_PERFTRACING_C_LIB) else(FEATURE_EVENT_TRACE) add_custom_target(eventing_headers) # add a dummy target to avoid checking for FEATURE_EVENT_TRACE in multiple places endif(FEATURE_EVENT_TRACE) diff --git a/src/coreclr/debug/debug-pal/CMakeLists.txt b/src/coreclr/debug/debug-pal/CMakeLists.txt index fa7219804bcfc..f7d036af5792b 100644 --- a/src/coreclr/debug/debug-pal/CMakeLists.txt +++ b/src/coreclr/debug/debug-pal/CMakeLists.txt @@ -4,26 +4,20 @@ include_directories(../../pal/inc) add_definitions(-DPAL_STDCPP_COMPAT) -if(FEATURE_PERFTRACING_C_LIB) - set(SHARED_EVENTPIPE_DIR ${CLR_SRC_NATIVE_DIR}/eventpipe) - include_directories(${SHARED_EVENTPIPE_DIR}) - add_definitions(-DFEATURE_CORECLR) - add_definitions(-DFEATURE_PERFTRACING) - add_definitions(-DFEATURE_PERFTRACING_C_LIB) - add_definitions(-DFEATURE_PERFTRACING_C_LIB_STANDALONE_PAL) -endif(FEATURE_PERFTRACING_C_LIB) + +set(SHARED_EVENTPIPE_DIR ${CLR_SRC_NATIVE_DIR}/eventpipe) +include_directories(${SHARED_EVENTPIPE_DIR}) +add_definitions(-DFEATURE_CORECLR) +add_definitions(-DFEATURE_PERFTRACING) +add_definitions(-DFEATURE_PERFTRACING_STANDALONE_PAL) if(CLR_CMAKE_HOST_WIN32) add_definitions(-DWIN32_LEAN_AND_MEAN) include_directories(../../inc) #needed for warning control if(CLR_CMAKE_TARGET_WIN32) - if(FEATURE_PERFTRACING_C_LIB) set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-win32.c") set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES LANGUAGE CXX) - else(FEATURE_PERFTRACING_C_LIB) - set (EVENTPIPE_PAL_SOURCES "win/diagnosticsipc.cpp") - endif(FEATURE_PERFTRACING_C_LIB) set(TWO_WAY_PIPE_SOURCES ${EVENTPIPE_PAL_SOURCES} @@ -42,13 +36,9 @@ if(CLR_CMAKE_HOST_UNIX) add_definitions(-DPAL_IMPLEMENTATION) add_definitions(-D_POSIX_C_SOURCE=200809L) - if(FEATURE_PERFTRACING_C_LIB) - set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-posix.c") - set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES LANGUAGE CXX) - set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES COMPILE_OPTIONS -xc++) - else(FEATURE_PERFTRACING_C_LIB) - set (EVENTPIPE_PAL_SOURCES "unix/diagnosticsipc.cpp") - endif(FEATURE_PERFTRACING_C_LIB) + set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-posix.c") + set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES LANGUAGE CXX) + set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES COMPILE_OPTIONS -xc++) set(TWO_WAY_PIPE_SOURCES ${EVENTPIPE_PAL_SOURCES} diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 70c64d8e52b9f..fd6072b65069d 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -30,19 +30,13 @@ if(FEATURE_JIT_PITCHING) endif(FEATURE_JIT_PITCHING) if(FEATURE_PERFTRACING) - if(FEATURE_PERFTRACING_C_LIB) set(SHARED_EVENTPIPE_DIR ${CLR_SRC_NATIVE_DIR}/eventpipe) set(CORECLR_EVENTPIPE_SHIM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/eventing/eventpipe) - else(FEATURE_PERFTRACING_C_LIB) - set(SHARED_EVENTPIPE_DIR ${VM_DIR}) - endif(FEATURE_PERFTRACING_C_LIB) endif(FEATURE_PERFTRACING) if(FEATURE_PERFTRACING) - if(FEATURE_PERFTRACING_C_LIB) include_directories(${SHARED_EVENTPIPE_DIR}) include_directories(${CORECLR_EVENTPIPE_SHIM_DIR}) - endif(FEATURE_PERFTRACING_C_LIB) endif(FEATURE_PERFTRACING) set(VM_SOURCES_DAC_AND_WKS_COMMON diff --git a/src/coreclr/vm/diagnosticserver.cpp b/src/coreclr/vm/diagnosticserver.cpp deleted file mode 100644 index ef026a1f40533..0000000000000 --- a/src/coreclr/vm/diagnosticserver.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "diagnosticserver.h" -#include "ipcstreamfactory.h" -#include "eventpipeprotocolhelper.h" -#include "dumpdiagnosticprotocolhelper.h" -#include "profilerdiagnosticprotocolhelper.h" -#include "processdiagnosticsprotocolhelper.h" -#include "diagnosticsprotocol.h" - -#ifdef TARGET_UNIX -#include "pal.h" -#endif // TARGET_UNIX - -#ifdef FEATURE_AUTO_TRACE -#include "autotrace.h" -#endif - -#ifdef FEATURE_PERFTRACING - -Volatile DiagnosticServer::s_shuttingDown(false); -CLREventStatic *DiagnosticServer::s_ResumeRuntimeStartupEvent = nullptr; -GUID DiagnosticsIpc::AdvertiseCookie_V1 = GUID_NULL; - -DWORD WINAPI DiagnosticServer::DiagnosticsServerThread(LPVOID) -{ - CONTRACTL - { -#ifndef DEBUG - NOTHROW; -#endif - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(s_shuttingDown || IpcStreamFactory::HasActivePorts()); - } - CONTRACTL_END; - - if (!IpcStreamFactory::HasActivePorts()) - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Diagnostics IPC listener was undefined\n"); - return 1; - } - - ErrorCallback LoggingCallback = [](const char *szMessage, uint32_t code) { - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, "warning (%d): %s.\n", code, szMessage); - }; - -#ifndef DEBUG - EX_TRY - { -#endif - while (!s_shuttingDown) - { - IpcStream *pStream = IpcStreamFactory::GetNextAvailableStream(LoggingCallback); - - if (pStream == nullptr) - continue; -#ifdef FEATURE_AUTO_TRACE - auto_trace_signal(); -#endif - DiagnosticsIpc::IpcMessage message; - if (!message.Initialize(pStream)) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_BAD_ENCODING); - delete pStream; - continue; - } - - if (::strcmp((char *)message.GetHeader().Magic, (char *)DiagnosticsIpc::DotnetIpcMagic_V1.Magic) != 0) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_MAGIC); - delete pStream; - continue; - } - - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "DiagnosticServer - received IPC message with command set (%d) and command id (%d)\n", message.GetHeader().CommandSet, message.GetHeader().CommandId); - - switch ((DiagnosticsIpc::DiagnosticServerCommandSet)message.GetHeader().CommandSet) - { - case DiagnosticsIpc::DiagnosticServerCommandSet::EventPipe: - EventPipeProtocolHelper::HandleIpcMessage(message, pStream); - break; - - case DiagnosticsIpc::DiagnosticServerCommandSet::Dump: - DumpDiagnosticProtocolHelper::HandleIpcMessage(message, pStream); - break; - - case DiagnosticsIpc::DiagnosticServerCommandSet::Process: - ProcessDiagnosticsProtocolHelper::HandleIpcMessage(message,pStream); - break; - -#ifdef FEATURE_PROFAPI_ATTACH_DETACH - case DiagnosticsIpc::DiagnosticServerCommandSet::Profiler: - ProfilerDiagnosticProtocolHelper::HandleIpcMessage(message, pStream); - break; -#endif // FEATURE_PROFAPI_ATTACH_DETACH - - default: - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet); - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND); - delete pStream; - break; - } - } -#ifndef DEBUG - } - EX_CATCH - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Exception caught in diagnostic thread. Leaving thread now.\n"); - } - EX_END_CATCH(SwallowAllExceptions); -#endif - - return 0; -} - -bool DiagnosticServer::Initialize() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // COMPlus_EnableDiagnostics==0 disables diagnostics so we don't create the diagnostics pipe/socket or diagnostics server thread - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableDiagnostics) == 0) - { - return true; - } - - bool fSuccess = false; - - EX_TRY - { - auto ErrorCallback = [](const char *szMessage, uint32_t code) { - STRESS_LOG2( - LF_DIAGNOSTICS_PORT, // facility - LL_ERROR, // level - "Failed to create diagnostic IPC: error (%d): %s.\n", // msg - code, // data1 - szMessage); // data2 - }; - - // Initialize the RuntimeIndentifier before use - CoCreateGuid(&DiagnosticsIpc::AdvertiseCookie_V1); - - // Ports can fail to be configured - bool fAnyErrors = IpcStreamFactory::Configure(ErrorCallback); - if (fAnyErrors) - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "At least one Diagnostic Port failed to be configured.\n"); - - if (IpcStreamFactory::AnySuspendedPorts()) - { - s_ResumeRuntimeStartupEvent = new CLREventStatic(); - s_ResumeRuntimeStartupEvent->CreateManualEvent(false); - } - - if (IpcStreamFactory::HasActivePorts()) - { -#ifdef FEATURE_AUTO_TRACE - auto_trace_init(); - auto_trace_launch(); -#endif - DWORD dwThreadId = 0; - HANDLE hServerThread = ::CreateThread( // TODO: Is it correct to have this "lower" level call here? - nullptr, // no security attribute - 0, // default stack size - DiagnosticsServerThread, // thread proc - nullptr, // thread parameter - 0, // not suspended - &dwThreadId); // returns thread ID - - if (hServerThread == NULL) - { - IpcStreamFactory::ClosePorts(); - - // Failed to create IPC thread. - STRESS_LOG1( - LF_DIAGNOSTICS_PORT, // facility - LL_ERROR, // level - "Failed to create diagnostic server thread (%d).\n", // msg - ::GetLastError()); // data1 - } - else - { - ::CloseHandle(hServerThread); - -#ifdef FEATURE_AUTO_TRACE - auto_trace_wait(); -#endif - fSuccess = true; - } - } - } - EX_CATCH - { - // TODO: Should we log anything here? - } - EX_END_CATCH(SwallowAllExceptions); - - return fSuccess; -} - -bool DiagnosticServer::Shutdown() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - bool fSuccess = false; - - s_shuttingDown = true; - - EX_TRY - { - if (IpcStreamFactory::HasActivePorts()) - { - auto ErrorCallback = [](const char *szMessage, uint32_t code) { - STRESS_LOG2( - LF_DIAGNOSTICS_PORT, // facility - LL_ERROR, // level - "Failed to close diagnostic IPC: error (%d): %s.\n", // msg - code, // data1 - szMessage); // data2 - }; - - IpcStreamFactory::Shutdown(ErrorCallback); - } - fSuccess = true; - } - EX_CATCH - { - fSuccess = false; - // TODO: Should we log anything here? - } - EX_END_CATCH(SwallowAllExceptions); - - return fSuccess; -} - -// This method will block runtime bring-up IFF DOTNET_DiagnosticsMonitorAddress != nullptr and DOTNET_DiagnosticsMonitorPauseOnStart!=0 (it's default state) -// The s_ResumeRuntimeStartupEvent event will be signaled when the Diagnostics Monitor uses the ResumeRuntime Diagnostics IPC Command -void DiagnosticServer::PauseForDiagnosticsMonitor() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if (IpcStreamFactory::AnySuspendedPorts()) - { - _ASSERTE(s_ResumeRuntimeStartupEvent != nullptr && s_ResumeRuntimeStartupEvent->IsValid()); - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, "The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command."); - const DWORD dwFiveSecondWait = s_ResumeRuntimeStartupEvent->Wait(5000, false); - if (dwFiveSecondWait == WAIT_TIMEOUT) - { - CLRConfigStringHolder dotnetDiagnosticPortString = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DOTNET_DiagnosticPorts); - WCHAR empty[] = W(""); - DWORD dotnetDiagnosticPortSuspend = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend); - wprintf(W("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port.\n")); - wprintf(W("DOTNET_DiagnosticPorts=\"%s\"\n"), dotnetDiagnosticPortString == nullptr ? empty : dotnetDiagnosticPortString.GetValue()); - wprintf(W("DOTNET_DefaultDiagnosticPortSuspend=%d\n"), dotnetDiagnosticPortSuspend); - fflush(stdout); - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, "The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command and has waited 5 seconds."); - const DWORD dwWait = s_ResumeRuntimeStartupEvent->Wait(INFINITE, false); - } - } - // allow wait failures to fall through and the runtime to continue coming up -} - -void DiagnosticServer::ResumeRuntimeStartup() -{ - LIMITED_METHOD_CONTRACT; - IpcStreamFactory::ResumeCurrentPort(); - if (!IpcStreamFactory::AnySuspendedPorts() && s_ResumeRuntimeStartupEvent != nullptr && s_ResumeRuntimeStartupEvent->IsValid()) - s_ResumeRuntimeStartupEvent->Set(); -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/diagnosticserver.h b/src/coreclr/vm/diagnosticserver.h deleted file mode 100644 index 3e001d600ea56..0000000000000 --- a/src/coreclr/vm/diagnosticserver.h +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __DIAGNOSTIC_SERVER_H__ -#define __DIAGNOSTIC_SERVER_H__ - -#ifdef FEATURE_PERFTRACING // This macro should change to something more generic than performance. - -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -//! Defines an implementation of a IPC handler that dispatches messages to the runtime. -class DiagnosticServer final -{ -public: - // Initialize the event pipe (Creates the EventPipe IPC server). - static bool Initialize(); - - // Shutdown the event pipe. - static bool Shutdown(); - - // Diagnostics server thread. - static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter); - - // Pauses EEStartup after the Diagnostics Server has been started - // allowing a Diagnostics Monitor to attach perform tasks before - // Startup is completed - NOINLINE static void PauseForDiagnosticsMonitor(); - - // Sets CLREvent to resume startup in EEStartupHelper (see: ceemain.cpp after DiagnosticServer::Initialize() for pausing point) - // This is a no-op if not configured to pause or runtime has already resumed - static void ResumeRuntimeStartup(); - -private: - static Volatile s_shuttingDown; - static CLREventStatic *s_ResumeRuntimeStartupEvent; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __DIAGNOSTIC_SERVER_H__ diff --git a/src/coreclr/vm/diagnosticserveradapter.h b/src/coreclr/vm/diagnosticserveradapter.h index 9c0b17f890d96..3e6e8b85bd9e1 100644 --- a/src/coreclr/vm/diagnosticserveradapter.h +++ b/src/coreclr/vm/diagnosticserveradapter.h @@ -6,50 +6,29 @@ #if defined(FEATURE_PERFTRACING) && !(CROSSGEN_COMPILE) -#ifdef FEATURE_PERFTRACING_C_LIB #include "ds-server.h" -#else -#include "palclr.h" -#include "diagnosticserver.h" -#endif class DiagnosticServerAdapter final { public: static inline bool Initialize() { -#ifdef FEATURE_PERFTRACING_C_LIB return ds_server_init(); -#else - return DiagnosticServer::Initialize(); -#endif } static inline bool Shutdown() { -#ifdef FEATURE_PERFTRACING_C_LIB return ds_server_shutdown(); -#else - return DiagnosticServer::Shutdown(); -#endif } NOINLINE static void PauseForDiagnosticsMonitor() { -#ifdef FEATURE_PERFTRACING_C_LIB return ds_server_pause_for_diagnostics_monitor(); -#else - return DiagnosticServer::PauseForDiagnosticsMonitor(); -#endif } static void ResumeRuntimeStartup() { -#ifdef FEATURE_PERFTRACING_C_LIB return ds_server_resume_runtime_startup(); -#else - return DiagnosticServer::ResumeRuntimeStartup(); -#endif } }; diff --git a/src/coreclr/vm/diagnosticsprotocol.h b/src/coreclr/vm/diagnosticsprotocol.h deleted file mode 100644 index 53e7413c04c8f..0000000000000 --- a/src/coreclr/vm/diagnosticsprotocol.h +++ /dev/null @@ -1,581 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __DIAGNOSTICS_PROTOCOL_H__ -#define __DIAGNOSTICS_PROTOCOL_H__ - -#ifdef FEATURE_PERFTRACING - -#include "clr_std/type_traits" -#include "new.hpp" -#include "diagnosticsipc.h" -#include "corerror.h" - -#define DOTNET_IPC_V1_MAGIC "DOTNET_IPC_V1" - -template -bool TryParse(uint8_t *&bufferCursor, uint32_t &bufferLen, T &result) -{ - static_assert( - std::is_integral::value || std::is_same::value || - std::is_same::value || std::is_same::value, - "Can only be instantiated with integral and floating point types."); - - if (bufferLen < sizeof(T)) - return false; - memcpy(&result, bufferCursor, sizeof(T)); - bufferCursor += sizeof(T); - bufferLen -= sizeof(T); - return true; -} - -template -bool TryParseString(uint8_t *&bufferCursor, uint32_t &bufferLen, const T *&result) -{ - static_assert( - std::is_same::value || std::is_same::value, - "Can only be instantiated with char and WCHAR types."); - - uint32_t stringLen = 0; - if (!TryParse(bufferCursor, bufferLen, stringLen)) - return false; - if (stringLen == 0) - { - result = nullptr; - return true; - } - if (stringLen > (bufferLen / sizeof(T))) - return false; - if ((reinterpret_cast(bufferCursor))[stringLen - 1] != 0) - return false; - result = reinterpret_cast(bufferCursor); - - const uint32_t TotalStringLength = stringLen * sizeof(T); - bufferCursor += TotalStringLength; - bufferLen -= TotalStringLength; - return true; -} - -namespace DiagnosticsIpc -{ - enum class IpcMagicVersion : uint8_t - { - DOTNET_IPC_V1 = 0x01, - // FUTURE - }; - - enum class DiagnosticServerCommandSet : uint8_t - { - // reserved = 0x00, - Dump = 0x01, - EventPipe = 0x02, - Profiler = 0x03, - Process = 0x04, - - Server = 0xFF, - }; - - enum class DiagnosticServerResponseId : uint8_t - { - OK = 0x00, - // future - Error = 0xFF, - }; - - struct MagicVersion - { - uint8_t Magic[14]; - }; - - // The header to be associated with every command and response - // to/from the diagnostics server - struct IpcHeader - { - union - { - MagicVersion _magic; - uint8_t Magic[14]; // Magic Version number; a 0 terminated char array - }; - uint16_t Size; // The size of the incoming packet, size = header + payload size - uint8_t CommandSet; // The scope of the Command. - uint8_t CommandId; // The command being sent - uint16_t Reserved; // reserved for future use - }; - - const MagicVersion DotnetIpcMagic_V1 = { "DOTNET_IPC_V1" }; - - /** - * ==ADVERTISE PROTOCOL== - * Before standard IPC Protocol communication can occur on a client-mode connection - * the runtime must advertise itself over the connection. ALL SUBSEQUENT COMMUNICATION - * IS STANDARD DIAGNOSTICS IPC PROTOCOL COMMUNICATION. - * - * See spec in: dotnet/diagnostics@documentation/design-docs/ipc-spec.md - * - * The flow for Advertise is a one-way burst of 34 bytes consisting of - * 8 bytes - "ADVR_V1\0" (ASCII chars + null byte) - * 16 bytes - random 128 bit number cookie (little-endian) - * 8 bytes - PID (little-endian) - * 2 bytes - unused 2 byte field for futureproofing - */ - - const uint8_t AdvertiseMagic_V1[8] = "ADVR_V1"; - - const uint32_t AdvertiseSize = 34; - - // initialized in DiagnosticServer::Initialize during EEStartupHelper - extern GUID AdvertiseCookie_V1; - - inline GUID GetAdvertiseCookie_V1() - { - return AdvertiseCookie_V1; - } - - inline bool SendIpcAdvertise_V1(IpcStream *pStream) - { - uint8_t advertiseBuffer[DiagnosticsIpc::AdvertiseSize]; - GUID cookie = GetAdvertiseCookie_V1(); - uint64_t pid = GetCurrentProcessId(); - - uint64_t *buffer = (uint64_t*)advertiseBuffer; - buffer[0] = *(uint64_t*)AdvertiseMagic_V1; - // fills buffer[1] and buffer[2] - memcpy(&buffer[1], &cookie, sizeof(cookie)); - buffer[3] = VAL64(pid); - - // zero out unused field - ((uint16_t*)advertiseBuffer)[16] = VAL16(0); - - uint32_t nBytesWritten = 0; - if (!pStream->Write(advertiseBuffer, sizeof(advertiseBuffer), nBytesWritten, 100 /* ms */)) - return false; - - _ASSERTE(nBytesWritten == sizeof(advertiseBuffer)); - return nBytesWritten == sizeof(advertiseBuffer); - } - - const IpcHeader GenericSuccessHeader = - { - { DotnetIpcMagic_V1 }, - (uint16_t)sizeof(IpcHeader), - (uint8_t)DiagnosticServerCommandSet::Server, - (uint8_t)DiagnosticServerResponseId::OK, - (uint16_t)0x0000 - }; - - const IpcHeader GenericErrorHeader = - { - { DotnetIpcMagic_V1 }, - (uint16_t)sizeof(IpcHeader), - (uint8_t)DiagnosticServerCommandSet::Server, - (uint8_t)DiagnosticServerResponseId::Error, - (uint16_t)0x0000 - }; - - // The Following structs are template, meta-programming to enable - // users of the IpcMessage class to get free serialization for fixed-size structures. - // They check that the template parameter has a member (or static) function that - // has a specified signature and returns true or false based on that check. - // - // std::enable_if (and enable_if_t) act as a compile time flag to enable or - // disable a template specialization based on a boolean value. - // - // The Has* structs can be used as the boolean check in std::enable_if to - // enable a specific overload of a function based on whether the template parameter - // has that member function. - // - // These "switches" can be used in a variety of ways, but are used in the function - // template parameters below, e.g., - // - // template ::value, const T*> = nullptr> - // const T* FnName(...) - // - // For more details on this pattern, look up "Substitution Failure Is Not An Error" or SFINAE - - // template meta-programming to check for bool(Flatten)(BYTE*&, uint16_t&) member function - template - struct HasFlatten - { - template struct Has; - template static std::true_type test(Has*); - template static std::false_type test(...); - static constexpr bool value = decltype(test(nullptr))::value; - }; - - // template meta-programming to check for uint16_t(GetSize)() member function - template - struct HasGetSize - { - template struct Has; - template static std::true_type test(Has*); - template static std::false_type test(...); - static constexpr bool value = decltype(test(nullptr))::value; - }; - - // template meta-programming to check for a const T*(TryParse)(BYTE*,uint16_t&) static function - template - struct HasTryParse - { - template struct Has; - template static std::true_type test(Has*); - template static std::false_type test(...); - static constexpr bool value = decltype(test(nullptr))::value; - }; - - // Encodes the messages sent and received by the Diagnostics Server. - // - // Payloads that are fixed-size structs don't require any custom functionality. - // - // Payloads that are NOT fixed-size simply need to implement the following methods: - // * uint16_t GetSize() -> should return the flattened size of the payload - // * bool Flatten(BYTE *lpBuffer) -> Should serialize and write the payload to the provided buffer - // * const T *TryParse(BYTE *lpBuffer, uint16_t& bufferLen) -> should decode payload or return nullptr - class IpcMessage - { - public: - - // empty constructor for default values. Use Initialize. - IpcMessage() - : m_pData(nullptr), m_Header(), m_Size(0) - { - LIMITED_METHOD_CONTRACT; - }; - - // Initialize an outgoing IpcMessage with a header and payload - template - bool Initialize(IpcHeader header, T& payload) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - m_Header = header; - - return FlattenImpl(payload); - }; - - // Initialize an outgoing IpcMessage for an error - bool Initialize(HRESULT error) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - return Initialize(GenericErrorHeader, error); - } - - // Initialize an incoming IpcMessage from a stream by parsing - // the header and payload. - // - // If either fail, this returns false, true otherwise - bool Initialize(::IpcStream* pStream) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - return TryParse(pStream); - } - - ~IpcMessage() - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - delete[] m_pData; - }; - - // Given a buffer, attempt to parse out a given payload type - // If a payload type is fixed-size, this will simply return - // a pointer to the buffer of data reinterpreted as a const pointer. - // Otherwise, your payload type should implement the following static method: - // > const T *TryParse(BYTE *lpBuffer) - // which this will call if it exists. - // - // user is expected to check for a nullptr in the error case for non fixed-size payloads - // user owns the memory returned and is expected to free it when finished - template - const T* TryParsePayload() - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - ASSERT(IsFlattened()); - return TryParsePayloadImpl(); - }; - - const IpcHeader& GetHeader() const - { - LIMITED_METHOD_CONTRACT; - - return m_Header; - }; - - bool Send(IpcStream* pStream) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - ASSERT(IsFlattened()); - uint32_t nBytesWritten; - bool success = pStream->Write(m_pData, m_Size, nBytesWritten); - - return nBytesWritten == m_Size && success; - }; - - // Send an Error message across the pipe. - // Will return false on failure of any step (init or send). - // Regardless of success of this function, the spec - // dictates that the connection be closed on error, - // so the user is expected to delete the IpcStream - // after handling error cases. - static bool SendErrorMessage(IpcStream* pStream, HRESULT error) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - IpcMessage errorMessage; - bool success = errorMessage.Initialize((int32_t)error); - if (success) - success = errorMessage.Send(pStream); - return success; - }; - private: - // Pointer to flattened buffer filled with: - // incoming message: payload (could be empty which would be nullptr) - // outgoing message: header + payload - BYTE* m_pData; - // header associated with this message - struct IpcHeader m_Header; - // The total size of the message (header + payload) - uint16_t m_Size; - - bool IsFlattened() const - { - LIMITED_METHOD_CONTRACT; - - return m_pData != NULL; - }; - - // Attempt to populate header and payload from a buffer. - // Payload is left opaque as a flattened buffer in m_pData - bool TryParse(::IpcStream* pStream) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - // Read out header first - uint32_t nBytesRead; - bool success = pStream->Read(&m_Header, sizeof(IpcHeader), nBytesRead); - if (!success || nBytesRead < sizeof(IpcHeader)) - { - return false; - } - - if (m_Header.Size < sizeof(IpcHeader)) - { - return false; - } - - m_Size = m_Header.Size; - - // Then read out payload to buffer - uint16_t payloadSize = m_Header.Size - sizeof(IpcHeader); - if (payloadSize != 0) - { - BYTE* temp_buffer = new (nothrow) BYTE[payloadSize]; - if (temp_buffer == nullptr) - { - // OOM - return false; - } - - success = pStream->Read(temp_buffer, payloadSize, nBytesRead); - if (!success || nBytesRead < payloadSize) - { - delete[] temp_buffer; - return false; - } - m_pData = temp_buffer; - } - - return true; - }; - - // Create a buffer of the correct size filled with - // header + payload. Correctly handles flattening of - // trivial structures, but uses a bool(Flatten)(void*) - // and uint16_t(GetSize)() when available. - - // Handles the case where the payload structure exposes Flatten - // and GetSize methods - template ::value && HasGetSize::value, int>::type = 0> - bool FlattenImpl(U& payload) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if (IsFlattened()) - return true; - - S_UINT16 temp_size = S_UINT16(0); - temp_size += sizeof(struct IpcHeader) + payload.GetSize(); - ASSERT(!temp_size.IsOverflow()); - - m_Size = temp_size.Value(); - uint16_t remainingBytes = temp_size.Value(); - - BYTE* temp_buffer = new (nothrow) BYTE[m_Size]; - if (temp_buffer == nullptr) - { - // OOM - return false; - } - - BYTE* temp_buffer_cursor = temp_buffer; - - m_Header.Size = m_Size; - - memcpy(temp_buffer_cursor, &m_Header, sizeof(struct IpcHeader)); - temp_buffer_cursor += sizeof(struct IpcHeader); - remainingBytes -= sizeof(struct IpcHeader); - - const bool fSuccess = payload.Flatten(temp_buffer_cursor, remainingBytes); - - ASSERT(m_pData == nullptr); - m_pData = temp_buffer; - - return fSuccess; - }; - - // handles the case where we were handed a struct with no Flatten or GetSize method - template ::value && !HasGetSize::value, int>::type = 0> - bool FlattenImpl(U& payload) - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if (IsFlattened()) - return true; - - S_UINT16 temp_size = S_UINT16(0); - temp_size += sizeof(struct IpcHeader) + sizeof(payload); - ASSERT(!temp_size.IsOverflow()); - - m_Size = temp_size.Value(); - - BYTE* temp_buffer = new (nothrow) BYTE[m_Size]; - if (temp_buffer == nullptr) - { - // OOM - return false; - } - - BYTE* temp_buffer_cursor = temp_buffer; - - m_Header.Size = m_Size; - - memcpy(temp_buffer_cursor, &m_Header, sizeof(struct IpcHeader)); - temp_buffer_cursor += sizeof(struct IpcHeader); - - memcpy(temp_buffer_cursor, &payload, sizeof(payload)); - - ASSERT(m_pData == nullptr); - m_pData = temp_buffer; - - return true; - }; - - template ::value, int>::type = 0> - const U* TryParsePayloadImpl() - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - uint16_t payloadSize = m_Size - (uint16_t)sizeof(IpcHeader); - const U* payload = U::TryParse(m_pData, payloadSize); - m_pData = nullptr; // user is expected to clean up buffer when finished with it - return payload; - }; - - template ::value, int>::type = 0> - const U* TryParsePayloadImpl() - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - const U* payload = reinterpret_cast(m_pData); - m_pData = nullptr; // user is expected to clean up buffer when finished with it - return payload; - }; - }; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __DIAGNOSTICS_PROTOCOL_H__ diff --git a/src/coreclr/vm/dumpdiagnosticprotocolhelper.cpp b/src/coreclr/vm/dumpdiagnosticprotocolhelper.cpp deleted file mode 100644 index 70caa5a9ac6af..0000000000000 --- a/src/coreclr/vm/dumpdiagnosticprotocolhelper.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "fastserializer.h" -#include "dumpdiagnosticprotocolhelper.h" -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -#ifdef FEATURE_PERFTRACING - -void DumpDiagnosticProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - switch ((DumpCommandId)message.GetHeader().CommandId) - { - case DumpCommandId::GenerateCoreDump: - DumpDiagnosticProtocolHelper::GenerateCoreDump(message, pStream); - break; - - default: - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet); - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND); - delete pStream; - break; - } -} - -const GenerateCoreDumpCommandPayload* GenerateCoreDumpCommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - } - CONTRACTL_END; - - GenerateCoreDumpCommandPayload* payload = new (nothrow) GenerateCoreDumpCommandPayload; - if (payload == nullptr) - { - // OOM - return nullptr; - } - - payload->incomingBuffer = lpBuffer; - uint8_t* pBufferCursor = payload->incomingBuffer; - uint32_t bufferLen = BufferSize; - if (!TryParseString(pBufferCursor, bufferLen, payload->dumpName) || - !::TryParse(pBufferCursor, bufferLen, payload->dumpType) || - !::TryParse(pBufferCursor, bufferLen, payload->diagnostics)) - { - delete payload; - return nullptr; - } - - return payload; -} - -void DumpDiagnosticProtocolHelper::GenerateCoreDump(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - if (pStream == nullptr) - return; - - NewHolder payload = message.TryParsePayload(); - if (payload == nullptr) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_BAD_ENCODING); - delete pStream; - return; - } - -#ifdef HOST_UNIX - MAKE_UTF8PTR_FROMWIDE_NOTHROW(szDumpName, payload->dumpName); - if (szDumpName != nullptr) - { - if (!PAL_GenerateCoreDump(szDumpName, payload->dumpType, payload->diagnostics)) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - delete pStream; - return; - } - } - else - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_OUTOFMEMORY); - delete pStream; - return; - } -#else - if (!GenerateCrashDump(payload->dumpName, payload->dumpType, payload->diagnostics)) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - delete pStream; - return; - } -#endif - - DiagnosticsIpc::IpcMessage successResponse; - HRESULT success = S_OK; - if (successResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, success)) - successResponse.Send(pStream); - delete pStream; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/dumpdiagnosticprotocolhelper.h b/src/coreclr/vm/dumpdiagnosticprotocolhelper.h deleted file mode 100644 index be8f7596245ec..0000000000000 --- a/src/coreclr/vm/dumpdiagnosticprotocolhelper.h +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __DUMP_DIAGNOSTIC_PROTOCOL_HELPER_H__ -#define __DUMP_DIAGNOSTIC_PROTOCOL_HELPER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include - - -class IpcStream; - -// The Diagnostic command set is 0x01 -enum class DumpCommandId : uint8_t -{ - // reserved = 0x00, - GenerateCoreDump = 0x01, - // future -}; - -struct GenerateCoreDumpCommandPayload -{ - NewArrayHolder incomingBuffer; - - // The protocol buffer is defined as: - // string - dumpName (UTF16) - // int - dumpType - // int - diagnostics - // returns - // ulong - status - LPCWSTR dumpName; - uint32_t dumpType; - uint32_t diagnostics; - static const GenerateCoreDumpCommandPayload* TryParse(BYTE* lpBuffer, uint16_t& BufferSize); -}; - -class DumpDiagnosticProtocolHelper -{ -public: - // IPC event handlers. - static void GenerateCoreDump(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); // `dotnet-dump collect` - static void HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream); - -private: - const static uint32_t IpcStreamReadBufferSize = 8192; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __DUMP_DIAGNOSTIC_PROTOCOL_HELPER_H__ diff --git a/src/coreclr/vm/eventing/eventpipe/CMakeLists.txt b/src/coreclr/vm/eventing/eventpipe/CMakeLists.txt index 2b1fc1a3a80ce..e6aadec07e787 100644 --- a/src/coreclr/vm/eventing/eventpipe/CMakeLists.txt +++ b/src/coreclr/vm/eventing/eventpipe/CMakeLists.txt @@ -41,114 +41,45 @@ set (EVENTPIPE_HEADERS "") set (CORECLR_EVENTPIPE_SHIM_SOURCES "") set (CORECLR_EVENTPIPE_SHIM_HEADERS "") -if(FEATURE_PERFTRACING_C_LIB) - - set (SHARED_EVENTPIPE_SOURCE_PATH "${SHARED_EVENTPIPE_DIR}") - set (CORECLR_EVENTPIPE_SHIM_SOURCE_PATH "${CORECLR_EVENTPIPE_SHIM_DIR}") - - include (${SHARED_EVENTPIPE_SOURCE_PATH}/CMakeLists.txt) - - list(APPEND EVENTPIPE_SOURCES - ${SHARED_EVENTPIPE_SOURCES} - ${SHARED_DIAGNOSTIC_SERVER_SOURCES} - ) - - list(APPEND EVENTPIPE_HEADERS - ${SHARED_EVENTPIPE_HEADERS} - ${SHARED_DIAGNOSTIC_SERVER_HEADERS} - ) - - addprefix(EVENTPIPE_SOURCES ${SHARED_EVENTPIPE_SOURCE_PATH} "${EVENTPIPE_SOURCES}") - addprefix(EVENTPIPE_HEADERS ${SHARED_EVENTPIPE_SOURCE_PATH} "${EVENTPIPE_HEADERS}") - - set_source_files_properties(${EVENTPIPE_SOURCES} PROPERTIES LANGUAGE CXX) - - if(CLR_CMAKE_HOST_UNIX) - set_source_files_properties(${EVENTPIPE_SOURCES} PROPERTIES COMPILE_OPTIONS -xc++) - endif(CLR_CMAKE_HOST_UNIX) - - list(APPEND CORECLR_EVENTPIPE_SHIM_SOURCES - ep-rt-coreclr.cpp - ) - - list(APPEND CORECLR_EVENTPIPE_SHIM_HEADERS - ds-rt-coreclr.h - ds-rt-types-coreclr.h - ep-rt-coreclr.h - ep-rt-config-coreclr.h - ep-rt-types-coreclr.h - ) - - addprefix(CORECLR_EVENTPIPE_SHIM_SOURCES ${CORECLR_EVENTPIPE_SHIM_SOURCE_PATH} "${CORECLR_EVENTPIPE_SHIM_SOURCES}") - addprefix(CORECLR_EVENTPIPE_SHIM_HEADERS ${CORECLR_EVENTPIPE_SHIM_SOURCE_PATH} "${CORECLR_EVENTPIPE_SHIM_HEADERS}") -else(FEATURE_PERFTRACING_C_LIB) - list(APPEND EVENTPIPE_SOURCES - eventpipe.cpp - eventpipeblock.cpp - eventpipebuffer.cpp - eventpipebuffermanager.cpp - eventpipecommontypes.cpp - eventpipeconfiguration.cpp - eventpipeevent.cpp - eventpipeeventinstance.cpp - eventpipeeventpayload.cpp - eventpipeeventsource.cpp - eventpipefile.cpp - eventpipejsonfile.cpp - eventpipemetadatagenerator.cpp - eventpipeprovider.cpp - eventpipethread.cpp - eventpipesession.cpp - eventpipesessionprovider.cpp - fastserializer.cpp - sampleprofiler.cpp - ) - - list(APPEND EVENTPIPE_HEADERS - eventpipe.h - eventpipeblock.h - eventpipebuffer.h - eventpipebuffermanager.h - eventpipecommontypes.h - eventpipeconfiguration.h - eventpipeevent.h - eventpipeeventinstance.h - eventpipeeventpayload.h - eventpipeeventsource.h - eventpipefile.h - eventpipejsonfile.h - eventpipemetadatagenerator.h - eventpipeprovider.h - eventpipesession.h - eventpipesessionprovider.h - eventpipethread.h - fastserializer.h - sampleprofiler.h - stackcontents.h - ) - - list(APPEND EVENTPIPE_SOURCES - diagnosticserver.cpp - dumpdiagnosticprotocolhelper.cpp - eventpipeprotocolhelper.cpp - ipcstreamfactory.cpp - processdiagnosticsprotocolhelper.cpp - profilerdiagnosticprotocolhelper.cpp - ) - - list(APPEND EVENTPIPE_HEADERS - diagnosticserver.h - diagnosticsprotocol.h - dumpdiagnosticprotocolhelper.h - eventpipeprotocolhelper.h - ipcstreamfactory.h - processdiagnosticsprotocolhelper.h - profilerdiagnosticprotocolhelper.h - ) - - addprefix(EVENTPIPE_SOURCES ${VM_DIR} "${EVENTPIPE_SOURCES}") - addprefix(EVENTPIPE_HEADERS ${VM_DIR} "${EVENTPIPE_HEADERS}") -endif(FEATURE_PERFTRACING_C_LIB) +set (SHARED_EVENTPIPE_SOURCE_PATH "${SHARED_EVENTPIPE_DIR}") +set (CORECLR_EVENTPIPE_SHIM_SOURCE_PATH "${CORECLR_EVENTPIPE_SHIM_DIR}") + +include (${SHARED_EVENTPIPE_SOURCE_PATH}/CMakeLists.txt) + +list(APPEND EVENTPIPE_SOURCES + ${SHARED_EVENTPIPE_SOURCES} + ${SHARED_DIAGNOSTIC_SERVER_SOURCES} +) + +list(APPEND EVENTPIPE_HEADERS + ${SHARED_EVENTPIPE_HEADERS} + ${SHARED_DIAGNOSTIC_SERVER_HEADERS} +) + +addprefix(EVENTPIPE_SOURCES ${SHARED_EVENTPIPE_SOURCE_PATH} "${EVENTPIPE_SOURCES}") +addprefix(EVENTPIPE_HEADERS ${SHARED_EVENTPIPE_SOURCE_PATH} "${EVENTPIPE_HEADERS}") + +set_source_files_properties(${EVENTPIPE_SOURCES} PROPERTIES LANGUAGE CXX) + +if(CLR_CMAKE_HOST_UNIX) + set_source_files_properties(${EVENTPIPE_SOURCES} PROPERTIES COMPILE_OPTIONS -xc++) +endif(CLR_CMAKE_HOST_UNIX) + +list(APPEND CORECLR_EVENTPIPE_SHIM_SOURCES + ep-rt-coreclr.cpp +) + +list(APPEND CORECLR_EVENTPIPE_SHIM_HEADERS + ds-rt-coreclr.h + ds-rt-types-coreclr.h + ep-rt-coreclr.h + ep-rt-config-coreclr.h + ep-rt-types-coreclr.h +) + +addprefix(CORECLR_EVENTPIPE_SHIM_SOURCES ${CORECLR_EVENTPIPE_SHIM_SOURCE_PATH} "${CORECLR_EVENTPIPE_SHIM_SOURCES}") +addprefix(CORECLR_EVENTPIPE_SHIM_HEADERS ${CORECLR_EVENTPIPE_SHIM_SOURCE_PATH} "${CORECLR_EVENTPIPE_SHIM_HEADERS}") + list(APPEND EVENTPIPE_SOURCES ${CORECLR_EVENTPIPE_SHIM_SOURCES} diff --git a/src/coreclr/vm/eventpipe.cpp b/src/coreclr/vm/eventpipe.cpp deleted file mode 100644 index 7673ca498c036..0000000000000 --- a/src/coreclr/vm/eventpipe.cpp +++ /dev/null @@ -1,1076 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "clrtypes.h" -#include "safemath.h" -#include "diagnosticsipc.h" -#include "eventpipe.h" -#include "eventpipebuffermanager.h" -#include "eventpipeconfiguration.h" -#include "eventpipeeventpayload.h" -#include "eventpipesessionprovider.h" -#include "eventpipeevent.h" -#include "eventpipeeventsource.h" -#include "eventpipefile.h" -#include "eventpipeprovider.h" -#include "eventpipesession.h" -#include "eventpipejsonfile.h" -#include "eventtracebase.h" -#include "ipcstreamfactory.h" -#include "sampleprofiler.h" -#include "win32threadpool.h" -#include "ceemain.h" -#include "configuration.h" - -#ifdef TARGET_UNIX -#include "pal.h" -#endif // TARGET_UNIX - -#ifdef FEATURE_PERFTRACING - -CrstStatic EventPipe::s_configCrst; -Volatile EventPipe::s_state(EventPipeState::NotInitialized); -EventPipeConfiguration EventPipe::s_config; -EventPipeEventSource *EventPipe::s_pEventSource = nullptr; -VolatilePtr EventPipe::s_pSessions[MaxNumberOfSessions]; -Volatile EventPipe::s_allowWrite = 0; -#ifndef TARGET_UNIX -unsigned int * EventPipe::s_pProcGroupOffsets = nullptr; -#endif -Volatile EventPipe::s_numberOfSessions(0); -CQuickArrayList EventPipe::s_rgDeferredEnableEventPipeSessionIds = CQuickArrayList(); -CQuickArrayList EventPipe::s_rgDeferredDisableEventPipeSessionIds = CQuickArrayList(); -bool EventPipe::s_CanStartThreads = false; - -// This function is auto-generated from /src/scripts/genEventPipe.py -#ifdef TARGET_UNIX -extern "C" -#endif -void InitProvidersAndEvents(); - -void EventPipe::Initialize() -{ - STANDARD_VM_CONTRACT; - - if (s_state != EventPipeState::NotInitialized) - { - _ASSERTE(!"EventPipe::Initialize was already initialized."); - return; - } - - const bool tracingInitialized = s_configCrst.InitNoThrow( - CrstEventPipe, - (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN | CRST_HOST_BREAKABLE)); - - EventPipeThread::Initialize(); - - // Initialize the session container to nullptr. - for (VolatilePtr &session : s_pSessions) - session.Store(nullptr); - - s_config.Initialize(); - - s_pEventSource = new EventPipeEventSource(); - - // This calls into auto-generated code to initialize the runtime providers - // and events so that the EventPipe configuration lock isn't taken at runtime - InitProvidersAndEvents(); - - // Set the sampling rate for the sample profiler. - const unsigned long DefaultProfilerSamplingRateInNanoseconds = 1000000; // 1 msec. - SampleProfiler::SetSamplingRate(DefaultProfilerSamplingRateInNanoseconds); - - - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeProcNumbers) != 0) - { -#ifndef TARGET_UNIX - // setup the windows processor group offset table - WORD numGroups = ::GetActiveProcessorGroupCount(); - s_pProcGroupOffsets = new (nothrow) unsigned int[numGroups]; - if (s_pProcGroupOffsets) - { - unsigned int countProcs = 0; - for (WORD i = 0; i < numGroups; i++) - { - s_pProcGroupOffsets[i] = countProcs; - countProcs += GetActiveProcessorCount(i); - } - } -#endif - } - - { - CrstHolder _crst(GetLock()); - if (tracingInitialized) - s_state = EventPipeState::Initialized; - } - EnableViaEnvironmentVariables(); -} - -// Finish setting up the rest of EventPipe. -void EventPipe::FinishInitialize() -{ - STANDARD_VM_CONTRACT; - - // Enable streaming for any deferred sessions - { - CrstHolder _crst(GetLock()); - - s_CanStartThreads = true; - - while (s_rgDeferredEnableEventPipeSessionIds.Size() > 0) - { - EventPipeSessionID id = s_rgDeferredEnableEventPipeSessionIds.Pop(); - if (IsSessionIdInCollection(id)) - { - EventPipeSession *pSession = reinterpret_cast(id); - pSession->StartStreaming(); - } - } - - SampleProfiler::CanStartSampling(); - } - - // release lock in case someone tried to disable while we held it - // s_rgDeferredDisableEventPipeSessionIds is now safe to access without the - // lock since we've set s_canStartThreads to true inside the lock. Anyone - // who was waiting on that lock will see that state and not mutate the defer list - while (s_rgDeferredDisableEventPipeSessionIds.Size() > 0) - { - EventPipeSessionID id = s_rgDeferredDisableEventPipeSessionIds.Pop(); - DisableHelper(id); - } -} - -// -// 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; - } - - LPWSTR configToParse = eventpipeConfig; - int providerCnt = 0; - - // Create EventPipeProviderConfiguration and start tracing. - NewArrayHolder pProviders = nullptr; - NewArrayHolder pConfigurations = 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 = 3; - pProviders = new EventPipeProviderConfiguration[providerCnt]; - pProviders[0] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntime"), 0x4c14fccbd, 5, nullptr); - pProviders[1] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntimePrivate"), 0x4002000b, 5, nullptr); - pProviders[2] = EventPipeProviderConfiguration(W("Microsoft-DotNETCore-SampleProfiler"), 0x0, 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]; - pConfigurations = new XplatEventLoggerConfiguration[providerCnt]; - int i = 0; - while (*configToParse != '\0') - { - auto end = wcschr(configToParse, comma); - pConfigurations[i].Parse(configToParse); - - // if we find any invalid configuration, do not trace. - if (!pConfigurations[i].IsValid()) - { - return; - } - - pProviders[i] = EventPipeProviderConfiguration( - pConfigurations[i].GetProviderName(), - pConfigurations[i].GetEnabledKeywordsMask(), - pConfigurations[i].GetLevel(), - pConfigurations[i].GetArgument() - ); - - ++i; - - if (end == nullptr) - { - break; - } - configToParse = end + 1; - } - } - - uint64_t sessionID = EventPipe::Enable( - outputPath, - eventpipeCircularBufferMB, - pProviders, - providerCnt, - EventPipeSessionType::File, - EventPipeSerializationFormat::NetTraceV4, - true, - nullptr - ); - EventPipe::StartStreaming(sessionID); - } -} - -void EventPipe::Shutdown() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(s_state != EventPipeState::ShuttingDown); - } - CONTRACTL_END; - - if (g_fProcessDetach) - { - // If g_fProcessDetach is true, all threads except this got ripped because someone called ExitProcess(). - // This check is an attempt recognize that case and skip all unsafe cleanup work. - - // Since event reading/writing could happen while that ExitProcess happened, the stream is probably - // screwed anyway. Therefore we do NOT attempt to flush buffer, do rundown. Cleaning up memory at this - // point is rather meaningless too since the process is going to terminate soon. Therefore we simply - // quickly exit here - - // TODO: Consider releasing the resources that could last longer than the process (e.g. the files) - return; - } - - if (s_state != EventPipeState::Initialized) - return; - - // We are shutting down, so if disabling EventPipe throws, we need to move along anyway. - EX_TRY - { - { - CrstHolder _crst(GetLock()); - s_state = EventPipeState::ShuttingDown; - } - - for (uint32_t i = 0; i < MaxNumberOfSessions; ++i) - { - EventPipeSession *pSession = s_pSessions[i].Load(); - if (pSession) - Disable(reinterpret_cast(pSession)); - } - - // dotnet/coreclr: issue 24850: EventPipe shutdown race conditions - // Deallocating providers/events here might cause AV if a WriteEvent - // was to occur. Thus, we are not doing this cleanup. - - // // Remove EventPipeEventSource first since it tries to use the data structures that we remove below. - // // We need to do this after disabling sessions since those try to write to EventPipeEventSource. - // delete s_pEventSource; - // s_pEventSource = nullptr; - // s_config.Shutdown(); - } - EX_CATCH {} - EX_END_CATCH(SwallowAllExceptions); -} - -EventPipeSessionID EventPipe::Enable( - LPCWSTR strOutputPath, - uint32_t circularBufferSizeInMB, - const EventPipeProviderConfiguration *pProviders, - uint32_t numProviders, - EventPipeSessionType sessionType, - EventPipeSerializationFormat format, - const bool rundownRequested, - IpcStream *const pStream, - EventPipeSessionSynchronousCallback callback) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(format < EventPipeSerializationFormat::Count); - PRECONDITION(sessionType == EventPipeSessionType::Synchronous || circularBufferSizeInMB > 0); - PRECONDITION(numProviders > 0 && pProviders != nullptr); - } - CONTRACTL_END; - - // If the state or arguments are invalid, bail here. - if (sessionType == EventPipeSessionType::File && strOutputPath == nullptr) - return 0; - if (sessionType == EventPipeSessionType::IpcStream && pStream == nullptr) - return 0; - - EventPipeSessionID sessionId = 0; - RunWithCallbackPostponed([&](EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) { - if (s_state != EventPipeState::Initialized) - return; - - const uint32_t SessionIndex = GenerateSessionIndex(); - if (SessionIndex >= EventPipe::MaxNumberOfSessions) - return; - - EventPipeSession *const pSession = new EventPipeSession( - SessionIndex, - strOutputPath, - pStream, - sessionType, - format, - rundownRequested, - circularBufferSizeInMB, - pProviders, - numProviders, - callback); - - const bool fSuccess = EnableInternal(pSession, pEventPipeProviderCallbackDataQueue); - if (fSuccess) - sessionId = reinterpret_cast(pSession); - else - delete pSession; - }); - - return sessionId; -} - -bool EventPipe::EnableInternal( - EventPipeSession *const pSession, - EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(s_state == EventPipeState::Initialized); - PRECONDITION(pSession != nullptr && pSession->IsValid()); - PRECONDITION(IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (pSession == nullptr || !pSession->IsValid()) - return false; - - // Return if the index is invalid. - if (pSession->GetIndex() >= MaxNumberOfSessions) - { - _ASSERTE(!"Session index was out of range."); - return false; - } - - if (s_numberOfSessions >= MaxNumberOfSessions) - { - _ASSERTE(!"Max number of sessions reached."); - return false; - } - - // Register the SampleProfiler the very first time. - SampleProfiler::Initialize(pEventPipeProviderCallbackDataQueue); - - // Enable the EventPipe EventSource. - s_pEventSource->Enable(pSession); - - // Save the session. - if (s_pSessions[pSession->GetIndex()].LoadWithoutBarrier() != nullptr) - { - _ASSERTE(!"Attempting to override an existing session."); - return false; - } - s_pSessions[pSession->GetIndex()].Store(pSession); - s_allowWrite |= pSession->GetMask(); - ++s_numberOfSessions; - - // Enable tracing. - s_config.Enable(*pSession, pEventPipeProviderCallbackDataQueue); - - if (SessionRequestedSampling(pSession)) - { - SampleProfiler::Enable(); - } - - return true; -} - -void EventPipe::StartStreaming(EventPipeSessionID id) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - CrstHolder _crst(GetLock()); - - if (!IsSessionIdInCollection(id)) - return; - - EventPipeSession *const pSession = reinterpret_cast(id); - - if (s_CanStartThreads) - { - pSession->StartStreaming(); - } - else - { - s_rgDeferredEnableEventPipeSessionIds.Push(id); - } - -} - -void EventPipe::Disable(EventPipeSessionID id) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // EventPipe::Disable is called synchronously since the diagnostics server is - // single threaded. HOWEVER, if the runtime was suspended in EEStartupHelper, - // then EventPipe::FinishInitialize might not have executed yet. Disabling a session - // needs to either happen before we resume or after initialization. We briefly take the - // lock to check s_CanStartThreads to check whether we've finished initialization. We - // also check whether we are still suspended in which case we can safely disable the session - // without deferral. - { - CrstHolder _crst(GetLock()); - if (!s_CanStartThreads && !IpcStreamFactory::AnySuspendedPorts()) - { - s_rgDeferredDisableEventPipeSessionIds.Push(id); - return; - } - } - - DisableHelper(id); -} - -void EventPipe::DisableHelper(EventPipeSessionID id) -{ - if (s_CanStartThreads) - SetupThread(); - - if (id == 0) - return; - - // Don't block GC during clean-up. - GCX_PREEMP(); - - RunWithCallbackPostponed([&](EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) { - if (s_numberOfSessions > 0) - DisableInternal(id, pEventPipeProviderCallbackDataQueue); - }); - -#ifdef DEBUG - if ((int)s_numberOfSessions == 0) - { - _ASSERTE(!MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled); - _ASSERTE(!MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled); - _ASSERTE(!MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled); - } -#endif -} - -static void LogProcessInformationEvent(EventPipeEventSource &eventSource) -{ - // Get the managed command line. - LPCWSTR pCmdLine = GetCommandLineForDiagnostics(); - - // Log the process information event. - eventSource.SendProcessInfo(pCmdLine); -} - -void EventPipe::DisableInternal(EventPipeSessionID id, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(id != 0); - PRECONDITION(s_numberOfSessions > 0); - PRECONDITION(IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (!IsSessionIdInCollection(id)) - return; - - // If the session was not found, then there is nothing else to do. - EventPipeSession *const pSession = reinterpret_cast(id); - - if (SessionRequestedSampling(pSession)) - { - // Disable the profiler. - SampleProfiler::Disable(); - } - - // Log the process information event. - LogProcessInformationEvent(*s_pEventSource); - - // Disable pSession tracing. - s_config.Disable(*pSession, pEventPipeProviderCallbackDataQueue); - - pSession->Disable(); // WriteAllBuffersToFile, and remove providers. - - // Do rundown before fully stopping the session unless rundown wasn't requested - if (pSession->RundownRequested() && s_CanStartThreads) - { - pSession->EnableRundown(); // Set Rundown provider. - - EventPipeThread *const pEventPipeThread = EventPipeThread::GetOrCreate(); - if (pEventPipeThread != nullptr) - { - pEventPipeThread->SetAsRundownThread(pSession); - { - s_config.Enable(*pSession, pEventPipeProviderCallbackDataQueue); - { - pSession->ExecuteRundown(); - } - s_config.Disable(*pSession, pEventPipeProviderCallbackDataQueue); - } - pEventPipeThread->SetAsRundownThread(nullptr); - } - else - { - _ASSERTE(!"Failed to get or create the EventPipeThread for rundown events."); - } - } - - s_allowWrite &= ~(pSession->GetMask()); - - // Remove the session from the array before calling SuspendWriteEvent. This way - // we can guarantee that either the event write got the pointer and will complete - // the write successfully, or it gets null and will bail. - _ASSERTE(s_pSessions[pSession->GetIndex()] == pSession); - s_pSessions[pSession->GetIndex()].Store(nullptr); - - pSession->SuspendWriteEvent(); - bool ignored; - pSession->WriteAllBuffersToFile(&ignored); // Flush the buffers to the stream/file - - --s_numberOfSessions; - - // Write a final sequence point to the file now that all events have - // been emitted. - pSession->WriteSequencePointUnbuffered(); - - delete pSession; - - // Providers can't be deleted during tracing because they may be needed when serializing the file. - s_config.DeleteDeferredProviders(); -} - -EventPipeSession *EventPipe::GetSession(EventPipeSessionID id) -{ - LIMITED_METHOD_CONTRACT; - - { - CrstHolder _crst(GetLock()); - - if (s_state == EventPipeState::NotInitialized) - { - _ASSERTE(!"EventPipe::GetSession invoked before EventPipe was initialized."); - return nullptr; - } - - return IsSessionIdInCollection(id) ? - reinterpret_cast(id) : nullptr; - } -} - -bool EventPipe::IsSessionEnabled(EventPipeSessionID id) -{ - LIMITED_METHOD_CONTRACT; - - const EventPipeSession *const pSession = reinterpret_cast(id); - return s_pSessions[pSession->GetIndex()].Load() != nullptr; -} - -EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(!IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - EventPipeProvider *pProvider = NULL; - RunWithCallbackPostponed([&](EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) { - pProvider = CreateProvider(providerName, pCallbackFunction, pCallbackData, pEventPipeProviderCallbackDataQueue); - }); - - NotifyProfilerProviderCreated(pProvider); - return pProvider; -} - -EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData, EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - return s_config.CreateProvider(providerName, - pCallbackFunction, - pCallbackData, - pEventPipeProviderCallbackDataQueue); -} - -EventPipeProvider *EventPipe::GetProvider(const SString &providerName) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - return s_config.GetProvider(providerName); -} - -void EventPipe::AddProviderToSession(EventPipeSessionProvider *pProvider, EventPipeSession *pSession) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - - CONTRACTL_END; - - if (pProvider == nullptr || pSession == nullptr) - { - return; - } - { - CrstHolder _crst(GetLock()); - - pSession->AddSessionProvider(pProvider); - } -} - -void EventPipe::DeleteProvider(EventPipeProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Take the lock to make sure that we don't have a race - // between disabling tracing and deleting a provider - // where we hold a provider after tracing has been disabled. - CrstHolder _crst(GetLock()); - - if (pProvider != NULL) - { - if (Enabled()) - { - // Save the provider until the end of the tracing session. - pProvider->SetDeleteDeferred(); - } - else - { - // Delete the provider now. - s_config.DeleteProvider(pProvider); - } - } -} - -void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - EventPipeEventPayload payload(pData, length); - WriteEventInternal(event, payload, pActivityId, pRelatedActivityId); -} - -void EventPipe::WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - EventPipeEventPayload payload(pEventData, eventDataCount); - WriteEventInternal(event, payload, pActivityId, pRelatedActivityId); -} - -void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // We can't proceed if tracing is not initialized. - if (s_state == EventPipeState::NotInitialized) - return; - - // Exit early if the event is not enabled. - if (!event.IsEnabled()) - return; - - // Get the current thread; - Thread *const pThread = GetThread(); - - // If the activity id isn't specified AND we are in a managed thread, pull it from the current thread. - // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL - if (pActivityId == nullptr && pThread != nullptr) - pActivityId = pThread->GetActivityId(); - - WriteEventInternal( - pThread, - event, - payload, - pActivityId, - pRelatedActivityId); -} - -void EventPipe::WriteEventInternal( - Thread *pThread, - EventPipeEvent &event, - EventPipeEventPayload &payload, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId, - Thread *pEventThread, - StackContents *pStack) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // We can't proceed if tracing is not initialized. - if (s_state == EventPipeState::NotInitialized) - return; - - EventPipeThread *const pEventPipeThread = EventPipeThread::GetOrCreate(); - if (pEventPipeThread == nullptr) - { - _ASSERTE(!"Failed to get or create an EventPipeThread."); - return; - } - - if (pEventPipeThread->IsRundownThread()) - { - EventPipeSession *const pRundownSession = pEventPipeThread->GetRundownSession(); - _ASSERTE(pRundownSession != nullptr); - _ASSERTE(pThread != nullptr); - - BYTE *pData = payload.GetFlatData(); - if (pThread != nullptr && pRundownSession != nullptr && pData != nullptr) - { - // EventPipeFile::WriteEvent needs to allocate a metadata event - // and can therefore throw. In this context we will silently - // fail rather than disrupt the caller - EX_TRY - { - _ASSERTE(pRundownSession != nullptr); - if (pRundownSession != nullptr) - pRundownSession->WriteEvent( - pThread, - event, - payload, - pActivityId, - pRelatedActivityId, - pEventThread, - pStack); - } - EX_CATCH {} - EX_END_CATCH(SwallowAllExceptions); - } - } - else - { - for (uint32_t i = 0; i < MaxNumberOfSessions; ++i) - { - if ((s_allowWrite & ((uint64_t)1 << i)) == 0) - continue; - - // Now that we know this session is probably live we pay the perf cost of the memory barriers - // Setting this flag lets a thread trying to do a concurrent disable that it is not safe to delete - // session ID i. The if check above also ensures that once the session is unpublished this thread - // will eventually stop ever storing ID i into the WriteInProgress flag. This is important to - // guarantee termination of the YIELD_WHILE loop in SuspendWriteEvents. - pEventPipeThread->SetSessionWriteInProgress(i); - { - EventPipeSession *const pSession = s_pSessions[i].Load(); - - // Disable is allowed to set s_pSessions[i] = NULL at any time and that may have occured in between - // the check and the load - if (pSession != nullptr) - pSession->WriteEvent( - pThread, - event, - payload, - pActivityId, - pRelatedActivityId, - pEventThread, - pStack); - } - // Do not reference pSession past this point, we are signaling Disable() that it is safe to - // delete it - pEventPipeThread->SetSessionWriteInProgress(UINT32_MAX); - } - } -} - -void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pEvent != nullptr); - } - CONTRACTL_END; - - EventPipeEventPayload payload(pData, length); - WriteEventInternal( - pSamplingThread, - *pEvent, - payload, - nullptr /* pActivityId */, - nullptr /* pRelatedActivityId */, - pTargetThread, - &stackContents - ); -} - -bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - Thread *pThread = GetThread(); - return (pThread != NULL) ? WalkManagedStackForThread(pThread, stackContents) : false; -} - -bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pThread != NULL); - } - CONTRACTL_END; - - // Calling into StackWalkFrames in preemptive mode violates the host contract, - // but this contract is not used on CoreCLR. - CONTRACT_VIOLATION(HostViolation); - - stackContents.Reset(); - - // Before we call into StackWalkFrames we need to mark GC_ON_TRANSITIONS as FALSE - // because under GCStress runs (GCStress=0x3), a GC will be triggered for every transition, - // which will cause the GC to try to walk the stack while we are in the middle of walking the stack. - bool gcOnTransitions = GC_ON_TRANSITIONS(FALSE); - - StackWalkAction swaRet = pThread->StackWalkFrames( - (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback, - &stackContents, - ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS); - - GC_ON_TRANSITIONS(gcOnTransitions); - return ((swaRet == SWA_DONE) || (swaRet == SWA_CONTINUE)); -} - -StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pData) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pCf != NULL); - PRECONDITION(pData != NULL); - } - CONTRACTL_END; - - // Get the IP. - UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC; - if (controlPC == 0) - { - if (pData->GetLength() == 0) - { - // This happens for pinvoke stubs on the top of the stack. - return SWA_CONTINUE; - } - } - - _ASSERTE(controlPC != 0); - - // Add the IP to the captured stack. - pData->Append( - controlPC, - pCf->GetFunction()); - - // Continue the stack walk. - return SWA_CONTINUE; -} - -uint32_t EventPipe::GenerateSessionIndex() -{ - LIMITED_METHOD_CONTRACT; - PRECONDITION(IsLockOwnedByCurrentThread()); - - for (uint32_t i = 0; i < MaxNumberOfSessions; ++i) - if (s_pSessions[i].LoadWithoutBarrier() == nullptr) - return i; - return MaxNumberOfSessions; -} - -bool EventPipe::IsSessionIdInCollection(EventPipeSessionID id) -{ - LIMITED_METHOD_CONTRACT; - PRECONDITION(id != 0); - PRECONDITION(IsLockOwnedByCurrentThread()); - - const EventPipeSession *const pSession = reinterpret_cast(id); - for (uint32_t i = 0; i < MaxNumberOfSessions; ++i) - { - if (s_pSessions[i] == pSession) - { - _ASSERTE(i == pSession->GetIndex()); - return true; - } - } - return false; -} - -bool EventPipe::SessionRequestedSampling(EventPipeSession *pSession) -{ - LIMITED_METHOD_CONTRACT; - - EventPipeSessionProviderIterator providerList = pSession->GetProviders(); - EventPipeSessionProvider *pProvider = nullptr; - while (providerList.Next(&pProvider)) - { - if (wcscmp(pProvider->GetProviderName(), W("Microsoft-DotNETCore-SampleProfiler")) == 0) - { - return true; - } - } - - return false; -} - -EventPipeEventInstance *EventPipe::GetNextEvent(EventPipeSessionID sessionID) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(!IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // Only fetch the next event if a tracing session exists. - // The buffer manager is not disposed until the process is shutdown. - EventPipeSession *const pSession = GetSession(sessionID); - return pSession ? pSession->GetNextEvent() : nullptr; -} - -HANDLE EventPipe::GetWaitHandle(EventPipeSessionID sessionID) -{ - LIMITED_METHOD_CONTRACT; - - EventPipeSession *const pSession = GetSession(sessionID); - return pSession ? pSession->GetWaitEvent()->GetHandleUNHOSTED() : 0; -} - -void EventPipe::InvokeCallback(EventPipeProviderCallbackData *pEventPipeProviderCallbackData) -{ -#if defined(HOST_OSX) && defined(HOST_ARM64) - auto jitWriteEnableHolder = PAL_JITWriteEnable(false); -#endif // defined(HOST_OSX) && defined(HOST_ARM64) - - EventPipeProvider::InvokeCallback(pEventPipeProviderCallbackData); -} - -EventPipeEventInstance *EventPipe::BuildEventMetadataEvent(EventPipeEventInstance &instance, unsigned int metadataId) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - return s_config.BuildEventMetadataEvent(instance, metadataId); -} - -#ifdef DEBUG -bool EventPipe::IsLockOwnedByCurrentThread() -{ - return GetLock()->OwnedByCurrentThread(); -} -#endif - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipe.h b/src/coreclr/vm/eventpipe.h deleted file mode 100644 index ae56b9c771e5e..0000000000000 --- a/src/coreclr/vm/eventpipe.h +++ /dev/null @@ -1,260 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_H__ -#define __EVENTPIPE_H__ - -#ifdef FEATURE_PERFTRACING -#include "common.h" -#include "eventpipecommontypes.h" -#include "stackcontents.h" - -class CrstStatic; -class CrawlFrame; -class EventPipeConfiguration; -class EventPipeEvent; -class EventPipeEventInstance; -class EventPipeFile; -class EventPipeEventSource; -class EventPipeProvider; -class EventPipeSession; -class EventPipeSessionProvider; -class IpcStream; -enum class EventPipeSessionType; -enum class EventPipeSerializationFormat; -class EventPipeEventPayload; -struct EventData; - -typedef uint64_t EventPipeSessionID; - -enum class EventPipeState : uint32_t -{ - NotInitialized, - Initialized, - ShuttingDown, -}; - -class EventPipe -{ - // Declare friends. - friend class EventPipeConfiguration; - friend class EventPipeFile; - friend class EventPipeProvider; - -public: - static const uint32_t MaxNumberOfSessions = 64; - - // Initialize the event pipe. - static void Initialize(); - static void FinishInitialize(); - - // Initialize environment variable based session - static void EnableViaEnvironmentVariables(); - - // Shutdown the event pipe. - static void Shutdown(); - - // Enable tracing via the event pipe. - static EventPipeSessionID Enable( - LPCWSTR strOutputPath, - uint32_t circularBufferSizeInMB, - const EventPipeProviderConfiguration *pProviders, - uint32_t numProviders, - EventPipeSessionType sessionType, - EventPipeSerializationFormat format, - const bool rundownRequested, - IpcStream *const pStream, - EventPipeSessionSynchronousCallback callback = nullptr); - - // Disable tracing via the event pipe. - static void Disable(EventPipeSessionID id); - - // Get the session for the specified session ID. - static EventPipeSession *GetSession(EventPipeSessionID id); - - static bool IsSessionEnabled(EventPipeSessionID id); - - // start sending the required events down the pipe - // starting with file header info and then buffered events - static void StartStreaming(EventPipeSessionID id); - - // Specifies whether or not the event pipe is enabled. - static bool Enabled() - { - LIMITED_METHOD_CONTRACT; - return (s_state.LoadWithoutBarrier() >= EventPipeState::Initialized) && (s_numberOfSessions.LoadWithoutBarrier() > 0); - } - - // Create a provider. - static EventPipeProvider *CreateProvider( - const SString &providerName, - EventPipeCallback pCallbackFunction = nullptr, - void *pCallbackData = nullptr); - - static EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData, EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue); - - // Get a provider. - static EventPipeProvider *GetProvider(const SString &providerName); - - static void AddProviderToSession(EventPipeSessionProvider *pProvider, EventPipeSession *pSession); - - // Delete a provider. - static void DeleteProvider(EventPipeProvider *pProvider); - - // Write out an event from a flat buffer. - // Data is written as a serialized blob matching the ETW serialization conventions. - static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); - - // Write out an event from an EventData array. - // Data is written as a serialized blob matching the ETW serialization conventions. - static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); - - // Write out a sample profile event. - static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0); - - // Get the managed call stack for the current thread. - static bool WalkManagedStackForCurrentThread(StackContents &stackContents); - - // Get the managed call stack for the specified thread. - static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents); - - // Get next event. - static EventPipeEventInstance *GetNextEvent(EventPipeSessionID sessionID); - - // Get the event handle that signals when new events are available. - static HANDLE GetWaitHandle(EventPipeSessionID sessionID); - -#ifdef DEBUG - static bool IsLockOwnedByCurrentThread(); -#endif - - template - static void RunWithCallbackPostponed(T f) - { - EventPipeProviderCallbackDataQueue eventPipeProviderCallbackDataQueue; - EventPipeProviderCallbackData eventPipeProviderCallbackData; - { - CrstHolder _crst(GetLock()); - f(&eventPipeProviderCallbackDataQueue); - } - - while (eventPipeProviderCallbackDataQueue.TryDequeue(&eventPipeProviderCallbackData)) - InvokeCallback(&eventPipeProviderCallbackData); - } - - // Returns the a number 0...N representing the processor number this thread is currently - // running on. If for any reason we can't tell then return 0xFFFFFFFF. - static unsigned int GetCurrentProcessorNumber() - { -#ifndef TARGET_UNIX - if (s_pProcGroupOffsets) - { - PROCESSOR_NUMBER procNum; - GetCurrentProcessorNumberEx(&procNum); - return s_pProcGroupOffsets[procNum.Group] + procNum.Number; - } -#endif - return 0xFFFFFFFF; - } - -private: - static void InvokeCallback(EventPipeProviderCallbackData *pEventPipeProviderCallbackData); - - // Get the event used to write metadata to the event stream. - static EventPipeEventInstance *BuildEventMetadataEvent(EventPipeEventInstance &instance, unsigned int metadataId); - - // The counterpart to WriteEvent which after the payload is constructed - static void WriteEventInternal( - EventPipeEvent &event, - EventPipeEventPayload &payload, - LPCGUID pActivityId = nullptr, - LPCGUID pRelatedActivityId = nullptr); - - static void WriteEventInternal( - Thread *pThread, - EventPipeEvent &event, - EventPipeEventPayload &payload, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId, - Thread *pEventThread = nullptr, - StackContents *pStack = nullptr); - - static void DisableHelper(EventPipeSessionID id); - - static void DisableInternal(EventPipeSessionID id, EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue); - - // Enable the specified EventPipe session. - static bool EnableInternal( - EventPipeSession *const pSession, - EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue); - - // Callback function for the stack walker. For each frame walked, this callback is invoked. - static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData); - - //! Helper function used to locate a free index in the range 0 - EventPipe::MaxNumberOfSessions - //! Returns EventPipe::MaxNumberOfSessions if there are no free indexes - static uint32_t GenerateSessionIndex(); - - static bool IsSessionIdInCollection(EventPipeSessionID id); - - template - static void ForEachSession(EventPipeSessionHandlerCallback callback) - { - LIMITED_METHOD_CONTRACT; - _ASSERTE(IsLockOwnedByCurrentThread()); - - for (VolatilePtr &session : s_pSessions) - { - // Entering EventPipe lock gave us a barrier, we don't need - // more of them - EventPipeSession *const pSession = session.LoadWithoutBarrier(); - if (pSession) - callback(*pSession); - } - } - - // Get the event pipe configuration lock. - static CrstStatic *GetLock() - { - LIMITED_METHOD_CONTRACT; - return &s_configCrst; - } - - static void NotifyProfilerProviderCreated(EventPipeProvider *pProvider) - { -#ifndef DACCESS_COMPILE - // Let the profiler know the provider has been created so it can register if it wants to - BEGIN_PIN_PROFILER(CORProfilerIsMonitoringEventPipe()); - g_profControlBlock.pProfInterface->EventPipeProviderCreated(pProvider); - END_PIN_PROFILER(); -#endif // DACCESS_COMPILE - } - - static bool SessionRequestedSampling(EventPipeSession *pSession); - - static CrstStatic s_configCrst; - static Volatile s_state; - static EventPipeConfiguration s_config; - static VolatilePtr s_pSessions[MaxNumberOfSessions]; - static Volatile s_allowWrite; - static EventPipeEventSource *s_pEventSource; - - static bool s_CanStartThreads; - - static CQuickArrayList s_rgDeferredEnableEventPipeSessionIds; - static CQuickArrayList s_rgDeferredDisableEventPipeSessionIds; - - //! Bitmask tracking EventPipe active sessions. - // in all groups preceding it. For example if there are three groups with sizes: - // 1, 7, 6 the table would be 0, 1, 8 -#ifndef TARGET_UNIX - static unsigned int * s_pProcGroupOffsets; -#endif - static Volatile s_numberOfSessions; -}; - -static_assert(EventPipe::MaxNumberOfSessions == 64, "Maximum number of EventPipe sessions is not 64."); - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_H__ diff --git a/src/coreclr/vm/eventpipeadapter.h b/src/coreclr/vm/eventpipeadapter.h index 69f5166d6dbcb..1510622a2658b 100644 --- a/src/coreclr/vm/eventpipeadapter.h +++ b/src/coreclr/vm/eventpipeadapter.h @@ -6,7 +6,6 @@ #if defined(FEATURE_PERFTRACING) && !(CROSSGEN_COMPILE) -#ifdef FEATURE_PERFTRACING_C_LIB #include "ep.h" #include "ep-provider.h" #include "ep-config.h" @@ -17,34 +16,7 @@ #include "ep-metadata-generator.h" #include "ep-event-payload.h" #include "ep-buffer-manager.h" -#else -#include "corprof.h" -#include "clrex.h" -#include "eventpipe.h" -#include "eventpipeprovider.h" -#include "eventpipeconfiguration.h" -#include "eventpipeeventinstance.h" -#include "eventpipesession.h" -#include "eventpipesessionprovider.h" -#include "eventpipemetadatagenerator.h" -#include "eventpipeeventpayload.h" -#include "eventpipebuffermanager.h" -#endif -#ifndef FEATURE_PERFTRACING_C_LIB -#define EP_SESSION_TYPE_FILE EventPipeSessionType::File -#define EP_SESSION_TYPE_LISTENER EventPipeSessionType::Listener -#define EP_SESSION_TYPE_SYNCHRONOUS EventPipeSessionType::Synchronous -#define EP_SERIALIZATION_FORMAT_NETTRACE_V4 EventPipeSerializationFormat::NetTraceV4 -#define EP_SERIALIZATION_FORMAT_COUNT EventPipeSerializationFormat::Count -#define EP_PARAMETER_TYPE_OBJECT EventPipeParameterType::Object -#define EP_EVENT_LEVEL_LOGALWAYS EventPipeEventLevel::LogAlways -#define EP_EVENT_LEVEL_CRITICAL EventPipeEventLevel::Critical -#define EP_EVENT_LEVEL_ERROR EventPipeEventLevel::Error -#define EP_EVENT_LEVEL_WARNING EventPipeEventLevel::Warning -#define EP_EVENT_LEVEL_INFORMATIONAL EventPipeEventLevel::Informational -#define EP_EVENT_LEVEL_VERBOSE EventPipeEventLevel::Verbose -#endif class EventPipeProviderConfigurationAdapter final { @@ -53,7 +25,17 @@ class EventPipeProviderConfigurationAdapter final { STATIC_CONTRACT_NOTHROW; -#ifdef FEATURE_PERFTRACING_C_LIB + // This static_assert will fail because EventPipeProviderConfiguration uses char8_t strings rather than char16_t strings. + // This method takes the COR_PRF variant and converts to char8_t strings, so it should be fine. + // Leaving the assert commented out here for posterity. + // + // static_assert(offsetof(EventPipeProviderConfiguration, provider_name) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, providerName) + // && offsetof(EventPipeProviderConfiguration, keywords) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, keywords) + // && offsetof(EventPipeProviderConfiguration, logging_level) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, loggingLevel) + // && offsetof(EventPipeProviderConfiguration, filter_data) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, filterData) + // && sizeof(EventPipeProviderConfiguration) == sizeof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG), + // "Layouts of EventPipeProviderConfiguration type and COR_PRF_EVENTPIPE_PROVIDER_CONFIG type do not match!"); + m_providerConfigs = new (nothrow) EventPipeProviderConfiguration[providerConfigsLen]; m_providerConfigsLen = providerConfigsLen; if (m_providerConfigs) { @@ -66,23 +48,11 @@ class EventPipeProviderConfigurationAdapter final ep_rt_utf16_to_utf8_string (reinterpret_cast(providerConfigs[i].filterData), -1)); } } -#else - static_assert(offsetof(EventPipeProviderConfiguration, m_pProviderName) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, providerName) - && offsetof(EventPipeProviderConfiguration, m_keywords) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, keywords) - && offsetof(EventPipeProviderConfiguration, m_loggingLevel) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, loggingLevel) - && offsetof(EventPipeProviderConfiguration, m_pFilterData) == offsetof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG, filterData) - && sizeof(EventPipeProviderConfiguration) == sizeof(COR_PRF_EVENTPIPE_PROVIDER_CONFIG), - "Layouts of EventPipeProviderConfiguration type and COR_PRF_EVENTPIPE_PROVIDER_CONFIG type do not match!"); - m_providerConfigs = reinterpret_cast(providerConfigs); - m_providerConfigsLen = providerConfigsLen; -#endif } ~EventPipeProviderConfigurationAdapter() { STATIC_CONTRACT_NOTHROW; - -#ifdef FEATURE_PERFTRACING_C_LIB if (m_providerConfigs) { for (uint32_t i = 0; i < m_providerConfigsLen; ++i) { ep_rt_utf8_string_free ((ep_char8_t *)ep_provider_config_get_provider_name (&m_providerConfigs[i])); @@ -90,7 +60,6 @@ class EventPipeProviderConfigurationAdapter final } delete [] m_providerConfigs; } -#endif } inline const EventPipeProviderConfiguration * GetProviderConfigs() const @@ -106,11 +75,7 @@ class EventPipeProviderConfigurationAdapter final } private: -#ifdef FEATURE_PERFTRACING_C_LIB EventPipeProviderConfiguration *m_providerConfigs; -#else - const EventPipeProviderConfiguration *m_providerConfigs; -#endif uint32_t m_providerConfigsLen; }; @@ -121,20 +86,12 @@ class EventPipeParameterDescAdapter final { STATIC_CONTRACT_NOTHROW; -#ifdef FEATURE_PERFTRACING_C_LIB #ifdef EP_INLINE_GETTER_SETTER static_assert(offsetof(EventPipeParameterDesc, type) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, type) && offsetof(EventPipeParameterDesc, element_type) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, elementType) && offsetof(EventPipeParameterDesc, name) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, name) && sizeof(EventPipeParameterDesc) == sizeof(COR_PRF_EVENTPIPE_PARAM_DESC), "Layouts of EventPipeParameterDesc type and COR_PRF_EVENTPIPE_PARAM_DESC type do not match!"); -#endif -#else - static_assert(offsetof(EventPipeParameterDesc, Type) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, type) - && offsetof(EventPipeParameterDesc, ElementType) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, elementType) - && offsetof(EventPipeParameterDesc, Name) == offsetof(COR_PRF_EVENTPIPE_PARAM_DESC, name) - && sizeof(EventPipeParameterDesc) == sizeof(COR_PRF_EVENTPIPE_PARAM_DESC), - "Layouts of EventPipeParameterDesc type and COR_PRF_EVENTPIPE_PARAM_DESC type do not match!"); #endif m_params = reinterpret_cast(params); m_paramsLen = paramsLen; @@ -164,18 +121,11 @@ class EventDataAdapter final { STATIC_CONTRACT_NOTHROW; -#ifdef FEATURE_PERFTRACING_C_LIB #ifdef EP_INLINE_GETTER_SETTER static_assert(offsetof(EventData, ptr) == offsetof(COR_PRF_EVENT_DATA, ptr) && offsetof(EventData, size) == offsetof(COR_PRF_EVENT_DATA, size) && sizeof(EventData) == sizeof(COR_PRF_EVENT_DATA), "Layouts of EventData type and COR_PRF_EVENT_DATA type do not match!"); -#endif -#else - static_assert(offsetof(EventData, Ptr) == offsetof(COR_PRF_EVENT_DATA, ptr) - && offsetof(EventData, Size) == offsetof(COR_PRF_EVENT_DATA, size) - && sizeof(EventData) == sizeof(COR_PRF_EVENT_DATA), - "Layouts of EventData type and COR_PRF_EVENT_DATA type do not match!"); #endif m_data = reinterpret_cast(data); m_dataLen = dataLen; @@ -203,7 +153,6 @@ class EventPipeAdapter final public: static inline void Initialize() { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -211,14 +160,10 @@ class EventPipeAdapter final CONTRACTL_END; ep_init(); -#else - EventPipe::Initialize(); -#endif } static inline void FinishInitialize() { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -226,14 +171,10 @@ class EventPipeAdapter final CONTRACTL_END; ep_finish_init(); -#else - EventPipe::FinishInitialize(); -#endif } static inline void Shutdown() { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -243,19 +184,12 @@ class EventPipeAdapter final CONTRACTL_END; ep_shutdown(); -#else - EventPipe::Shutdown(); -#endif } static inline bool Enabled() { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_enabled(); -#else - return EventPipe::Enabled(); -#endif } static inline EventPipeSessionID Enable( @@ -268,7 +202,6 @@ class EventPipeAdapter final IpcStream *const stream, EventPipeSessionSynchronousCallback callback) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -292,23 +225,10 @@ class EventPipeAdapter final callback); ep_rt_utf8_string_free (outputPathUTF8); return result; -#else - return EventPipe::Enable ( - outputPath, - circularBufferSizeInMB, - providerConfigs.GetProviderConfigs(), - providerConfigs.GetProviderConfigsLen(), - sessionType, - format, - rundownRequested, - stream, - callback); -#endif } static inline void Disable(EventPipeSessionID id) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -318,14 +238,10 @@ class EventPipeAdapter final CONTRACTL_END; ep_disable(id); -#else - EventPipe::Disable(id); -#endif } static inline void StartStreaming(EventPipeSessionID id) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -335,34 +251,22 @@ class EventPipeAdapter final CONTRACTL_END; ep_start_streaming(id); -#else - EventPipe::StartStreaming(id); -#endif } static inline EventPipeSession * GetSession(EventPipeSessionID id) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_get_session(id); -#else - return EventPipe::GetSession(id); -#endif } static inline HANDLE GetWaitHandle(EventPipeSessionID id) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return reinterpret_cast(ep_get_wait_handle(id)); -#else - return EventPipe::GetWaitHandle(id); -#endif } static inline FILETIME GetSessionStartTime(EventPipeSession *session) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; FILETIME fileTime; @@ -373,28 +277,18 @@ class EventPipeAdapter final fileTime.dwLowDateTime = largeValue.u.LowPart; fileTime.dwHighDateTime = largeValue.u.HighPart; return fileTime; -#else - _ASSERTE(session != NULL); - return session->GetStartTime(); -#endif } static inline LONGLONG GetSessionStartTimestamp(EventPipeSession *session) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; _ASSERTE(session != NULL); return ep_session_get_session_start_timestamp(session); -#else - _ASSERTE(session != NULL); - return session->GetStartTimeStamp().QuadPart; -#endif } static inline void AddProviderToSession(EventPipeSessionProvider *provider, EventPipeSession *session) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -404,14 +298,10 @@ class EventPipeAdapter final CONTRACTL_END; ep_add_provider_to_session (provider, session); -#else - EventPipe::AddProviderToSession(provider, session); -#endif } static inline EventPipeProvider * CreateProvider(const SString &providerName, EventPipeCallback callback) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -424,14 +314,10 @@ class EventPipeAdapter final EventPipeProvider * provider = ep_create_provider (providerNameUTF8, callback, NULL, NULL); ep_rt_utf8_string_free (providerNameUTF8); return provider; -#else - return EventPipe::CreateProvider(providerName, callback, NULL); -#endif } static inline void DeleteProvider (EventPipeProvider * provider) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -441,14 +327,10 @@ class EventPipeAdapter final CONTRACTL_END; ep_delete_provider (provider); -#else - EventPipe::DeleteProvider(provider); -#endif } static inline EventPipeProvider * GetProvider (LPCWSTR providerName) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -464,14 +346,10 @@ class EventPipeAdapter final EventPipeProvider * provider = ep_get_provider (providerNameUTF8); ep_rt_utf8_string_free(providerNameUTF8); return provider; -#else - return EventPipe::GetProvider(providerName); -#endif } static EventPipeSessionProvider * CreateSessionProvider(const EventPipeProviderConfigurationAdapter &providerConfig) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -479,31 +357,21 @@ class EventPipeAdapter final MODE_ANY; } CONTRACTL_END; -#endif _ASSERTE (providerConfig.GetProviderConfigs() != NULL && providerConfig.GetProviderConfigsLen() == 1); const EventPipeProviderConfiguration *config = providerConfig.GetProviderConfigs(); if (!config) return NULL; -#ifdef FEATURE_PERFTRACING_C_LIB return ep_session_provider_alloc ( ep_provider_config_get_provider_name (&config[0]), ep_provider_config_get_keywords (&config[0]), (EventPipeEventLevel)ep_provider_config_get_logging_level (&config[0]), ep_provider_config_get_filter_data (&config[0])); -#else - return new EventPipeSessionProvider( - config[0].GetProviderName(), - config[0].GetKeywords(), - (EventPipeEventLevel)config[0].GetLevel(), - config[0].GetFilterData()); -#endif } static HRESULT GetProviderName(const EventPipeProvider *provider, ULONG numNameChars, ULONG *numNameCharsOut, LPWSTR name) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -525,36 +393,6 @@ class EventPipeAdapter final else if (name) memcpy (name, providerName, numProviderNameChars * sizeof (ep_char16_t)); } -#else - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(provider != NULL); - - HRESULT hr = S_OK; - const SString &providerName = provider->GetProviderName(); - ULONG numProviderNameChars = providerName.GetCount() + 1; - if (numNameCharsOut != NULL) - *numNameCharsOut = numProviderNameChars; - - if (numProviderNameChars >= numNameChars) { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - } else if (name) { - size_t pos = 0; - for (SString::CIterator it = providerName.Begin(); it != providerName.End(); ++it) - { - name[pos] = *it; - ++pos; - } - - name[pos] = '\0'; - } -#endif return hr; } @@ -569,7 +407,6 @@ class EventPipeAdapter final const EventPipeParameterDescAdapter ¶ms, bool needStack) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -603,43 +440,6 @@ class EventPipeAdapter final ep_rt_byte_array_free(metadata); } return realEvent; -#else - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(provider != NULL); - - size_t metadataLen = 0; - EventPipeEvent *realEvent = NULL; - NewArrayHolder metadata = EventPipeMetadataGenerator::GenerateEventMetadata( - eventID, - eventName, - keywords, - eventVersion, - (EventPipeEventLevel)level, - opcode, - (EventPipeParameterDesc *)params.GetParams(), - params.GetParamsLen(), - &metadataLen); - - if (metadata != NULL) { - // Add the event. - realEvent = provider->AddEvent( - eventID, - keywords, - eventVersion, - (EventPipeEventLevel)level, - needStack, - metadata, - (uint32_t)metadataLen); - } - return realEvent; -#endif } static inline EventPipeEvent * AddEvent( @@ -652,7 +452,6 @@ class EventPipeAdapter final BYTE *metadata = NULL, uint32_t metadataLen = 0) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -662,10 +461,6 @@ class EventPipeAdapter final CONTRACTL_END; return ep_provider_add_event(provider, eventID, keywords, eventVersion, level, needStack, metadata, metadataLen); -#else - _ASSERTE(provider != NULL); - return provider->AddEvent(eventID, keywords, eventVersion, level, needStack, metadata, metadataLen); -#endif } static inline void WriteEvent( @@ -675,7 +470,6 @@ class EventPipeAdapter final LPCGUID activityId, LPCGUID relatedActivityId) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -690,14 +484,6 @@ class EventPipeAdapter final dataLen, reinterpret_cast(activityId), reinterpret_cast(relatedActivityId)); -#else - EventPipe::WriteEvent( - *ep_event, - data, - dataLen, - activityId, - relatedActivityId); -#endif } static inline void WriteEvent( @@ -707,7 +493,6 @@ class EventPipeAdapter final LPCGUID activityId, LPCGUID relatedActivityId) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -722,14 +507,6 @@ class EventPipeAdapter final dataLen, reinterpret_cast(activityId), reinterpret_cast(relatedActivityId)); -#else - EventPipe::WriteEvent( - *ep_event, - data, - dataLen, - activityId, - relatedActivityId); -#endif } static inline void WriteEvent( @@ -748,17 +525,12 @@ class EventPipeAdapter final static inline bool EventIsEnabled (const EventPipeEvent *epEvent) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_is_enabled(epEvent); -#else - return epEvent->IsEnabled(); -#endif } static inline EventPipeEventInstance * GetNextEvent (EventPipeSessionID id) { -#ifdef FEATURE_PERFTRACING_C_LIB CONTRACTL { NOTHROW; @@ -768,113 +540,68 @@ class EventPipeAdapter final CONTRACTL_END; return ep_get_next_event(id); -#else - return EventPipe::GetNextEvent(id); -#endif } static inline EventPipeProvider * GetEventProvider (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_get_provider(ep_event_instance_get_ep_event(eventInstance)); -#else - return eventInstance->GetEvent()->GetProvider(); -#endif } static inline uint32_t GetEventID (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_get_event_id(ep_event_instance_get_ep_event(eventInstance)); -#else - return eventInstance->GetEvent()->GetEventID(); -#endif } static inline uint64_t GetEventThreadID (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_instance_get_thread_id(eventInstance); -#else - return eventInstance->GetThreadId64(); -#endif } static inline int64_t GetEventTimestamp (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_instance_get_timestamp(eventInstance); -#else - return eventInstance->GetTimeStamp()->QuadPart; -#endif } static inline LPCGUID GetEventActivityID (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; static_assert(sizeof(GUID) == EP_ACTIVITY_ID_SIZE, "Size missmatch, sizeof(GUID) should be equal to EP_ACTIVITY_ID_SIZE"); return reinterpret_cast(ep_event_instance_get_activity_id_cref(eventInstance)); -#else - return eventInstance->GetActivityId(); -#endif } static inline LPCGUID GetEventRelativeActivityID (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; static_assert(sizeof(GUID) == EP_ACTIVITY_ID_SIZE, "Size missmatch, sizeof(GUID) should be equal to EP_ACTIVITY_ID_SIZE"); return reinterpret_cast(ep_event_instance_get_related_activity_id_cref(eventInstance)); -#else - return eventInstance->GetRelatedActivityId(); -#endif } static inline const BYTE * GetEventData (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_instance_get_data(eventInstance); -#else - return eventInstance->GetData(); -#endif } static inline uint32_t GetEventDataLen (EventPipeEventInstance *eventInstance) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; return ep_event_instance_get_data_len(eventInstance); -#else - return eventInstance->GetDataLength(); -#endif } static inline void ResumeSession (EventPipeSession *session) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; ep_session_resume (session); -#else - _ASSERTE(session != NULL); - session->Resume(); -#endif } static inline void PauseSession (EventPipeSession *session) { -#ifdef FEATURE_PERFTRACING_C_LIB STATIC_CONTRACT_NOTHROW; ep_session_pause (session); -#else - _ASSERTE(session != NULL); - session->Pause(); -#endif } }; diff --git a/src/coreclr/vm/eventpipeadaptertypes.h b/src/coreclr/vm/eventpipeadaptertypes.h index d843f26e67b5e..f473bde10488d 100644 --- a/src/coreclr/vm/eventpipeadaptertypes.h +++ b/src/coreclr/vm/eventpipeadaptertypes.h @@ -6,17 +6,10 @@ #if defined(FEATURE_PERFTRACING) -#ifdef FEATURE_PERFTRACING_C_LIB typedef struct _EventFilterDescriptor EventFilterDescriptor; typedef struct _EventPipeBufferList EventPipeBufferList; typedef struct _EventPipeProvider EventPipeProvider; typedef struct _EventPipeSession EventPipeSession; -#else -struct EventFilterDescriptor; -class EventPipeBufferList; -class EventPipeProvider; -class EventPipeSession; -#endif //FEATURE_PERFTRACING_C_LIB #endif // FEATURE_PERFTRACING #endif // __EVENTPIPE_ADAPTER_TYPES_H__ diff --git a/src/coreclr/vm/eventpipeblock.cpp b/src/coreclr/vm/eventpipeblock.cpp deleted file mode 100644 index 66a06ff311438..0000000000000 --- a/src/coreclr/vm/eventpipeblock.cpp +++ /dev/null @@ -1,464 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeblock.h" -#include "eventpipeeventinstance.h" -#include "fastserializableobject.h" -#include "fastserializer.h" - -// my attempts to include limits.h were hitting missing headers on Linux -// This might be resolvable with more effort but I chose not to head -// down the rabbit hole when a perfectly decent 60 second fix was available: -#ifndef LLONG_MIN -#define LLONG_MIN 0x8000000000000000 -#endif -#ifndef LLONG_MAX -#define LLONG_MAX 0x7FFFFFFFFFFFFFFF -#endif - -#ifdef FEATURE_PERFTRACING - - - -DWORD GetBlockVersion(EventPipeSerializationFormat format) -{ - LIMITED_METHOD_CONTRACT; - switch (format) - { - case EventPipeSerializationFormat::NetPerfV3: - return 1; - case EventPipeSerializationFormat::NetTraceV4: - return 2; - default: - _ASSERTE(!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - -DWORD GetBlockMinVersion(EventPipeSerializationFormat format) -{ - LIMITED_METHOD_CONTRACT; - switch (format) - { - case EventPipeSerializationFormat::NetPerfV3: - return 0; - case EventPipeSerializationFormat::NetTraceV4: - return 2; - default: - _ASSERTE(!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - -EventPipeBlock::EventPipeBlock(unsigned int maxBlockSize, EventPipeSerializationFormat format) : - FastSerializableObject(GetBlockVersion(format), GetBlockMinVersion(format), format >= EventPipeSerializationFormat::NetTraceV4) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_pBlock = new (nothrow) BYTE[maxBlockSize]; - if (m_pBlock == NULL) - { - return; - } - - memset(m_pBlock, 0, maxBlockSize); - m_pWritePointer = m_pBlock; - m_pEndOfTheBuffer = m_pBlock + maxBlockSize; - m_format = format; -} - -EventPipeBlock::~EventPipeBlock() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - delete[] m_pBlock; -} - -void EventPipeBlock::Clear() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pBlock == NULL) - { - return; - } - - _ASSERTE(m_pWritePointer <= m_pEndOfTheBuffer); - - memset(m_pBlock, 0, GetSize()); - m_pWritePointer = m_pBlock; -} - -EventPipeEventBlockBase::EventPipeEventBlockBase(unsigned int maxBlockSize, EventPipeSerializationFormat format, bool fUseHeaderCompression) : - EventPipeBlock(maxBlockSize, format), m_fUseHeaderCompression(fUseHeaderCompression) -{ - memset(m_compressedHeader, 0, 100); - Clear(); -} - -void EventPipeEventBlockBase::Clear() -{ - EventPipeBlock::Clear(); - m_lastHeader.MetadataId = 0; - m_lastHeader.SequenceNumber = 0; - m_lastHeader.ThreadId = 0; - m_lastHeader.CaptureThreadId = 0; - m_lastHeader.StackId = 0; - m_lastHeader.TimeStamp.QuadPart = 0; - m_lastHeader.ActivityId = { 0 }; - m_lastHeader.RelatedActivityId = { 0 }; - m_lastHeader.DataLength = 0; - - m_minTimeStamp.QuadPart = LLONG_MAX; - m_maxTimeStamp.QuadPart = LLONG_MIN; -} - -void WriteVarUInt32(BYTE* & pWritePointer, unsigned int value) -{ - while (value >= 0x80) - { - *pWritePointer = (BYTE)(value | 0x80); - pWritePointer++; - value >>= 7; - } - *pWritePointer = (BYTE)value; - pWritePointer++; -} - -void WriteVarUInt64(BYTE* & pWritePointer, ULONGLONG value) -{ - while (value >= 0x80) - { - *pWritePointer = (BYTE)(value | 0x80); - pWritePointer++; - value >>= 7; - } - *pWritePointer = (BYTE)value; - pWritePointer++; -} - -bool EventPipeEventBlockBase::WriteEvent(EventPipeEventInstance &instance, - ULONGLONG captureThreadId, - unsigned int sequenceNumber, - DWORD stackId, - BOOL isSortedEvent) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(isSortedEvent || m_format >= EventPipeSerializationFormat::NetTraceV4); - } - CONTRACTL_END; - - if (m_pBlock == NULL) - { - return false; - } - - unsigned int dataLength = 0; - BYTE* alignedEnd = NULL; - unsigned int captureProcNumber = instance.GetProcNumber(); - - if (!m_fUseHeaderCompression) - { - unsigned int totalSize = instance.GetAlignedTotalSize(m_format); - if (m_pWritePointer + totalSize >= m_pEndOfTheBuffer) - { - return false; - } - - alignedEnd = m_pWritePointer + totalSize + sizeof(totalSize); - - memcpy(m_pWritePointer, &totalSize, sizeof(totalSize)); - m_pWritePointer += sizeof(totalSize); - - unsigned int metadataId = instance.GetMetadataId(); - _ASSERTE((metadataId & (1 << 31)) == 0); - metadataId |= (!isSortedEvent ? 1 << 31 : 0); - memcpy(m_pWritePointer, &metadataId, sizeof(metadataId)); - m_pWritePointer += sizeof(metadataId); - - if (m_format == EventPipeSerializationFormat::NetPerfV3) - { - DWORD threadId = instance.GetThreadId32(); - memcpy(m_pWritePointer, &threadId, sizeof(threadId)); - m_pWritePointer += sizeof(threadId); - } - else if (m_format == EventPipeSerializationFormat::NetTraceV4) - { - memcpy(m_pWritePointer, &sequenceNumber, sizeof(sequenceNumber)); - m_pWritePointer += sizeof(sequenceNumber); - - ULONGLONG threadId = instance.GetThreadId64(); - memcpy(m_pWritePointer, &threadId, sizeof(threadId)); - m_pWritePointer += sizeof(threadId); - - memcpy(m_pWritePointer, &captureThreadId, sizeof(captureThreadId)); - m_pWritePointer += sizeof(captureThreadId); - - memcpy(m_pWritePointer, &captureProcNumber, sizeof(captureProcNumber)); - m_pWritePointer += sizeof(captureProcNumber); - - memcpy(m_pWritePointer, &stackId, sizeof(stackId)); - m_pWritePointer += sizeof(stackId); - } - - const LARGE_INTEGER* timeStamp = instance.GetTimeStamp(); - memcpy(m_pWritePointer, timeStamp, sizeof(*timeStamp)); - m_pWritePointer += sizeof(*timeStamp); - - const GUID* activityId = instance.GetActivityId(); - memcpy(m_pWritePointer, activityId, sizeof(*activityId)); - m_pWritePointer += sizeof(*activityId); - - const GUID* relatedActivityId = instance.GetRelatedActivityId(); - memcpy(m_pWritePointer, relatedActivityId, sizeof(*relatedActivityId)); - m_pWritePointer += sizeof(*relatedActivityId); - - dataLength = instance.GetDataLength(); - memcpy(m_pWritePointer, &dataLength, sizeof(dataLength)); - m_pWritePointer += sizeof(dataLength); - } - else // using header compression - { - BYTE flags = 0; - BYTE* pWritePointer = m_compressedHeader; - - if (instance.GetMetadataId() != m_lastHeader.MetadataId) - { - WriteVarUInt32(pWritePointer, instance.GetMetadataId()); - flags |= 1; - } - if (isSortedEvent) - { - flags |= (1 << 6); - } - - if (m_lastHeader.SequenceNumber + (instance.GetMetadataId() != 0 ? 1 : 0) != sequenceNumber || - m_lastHeader.CaptureThreadId != captureThreadId || - m_lastHeader.CaptureProcNumber != captureProcNumber) - { - WriteVarUInt32(pWritePointer, sequenceNumber - m_lastHeader.SequenceNumber - 1); - WriteVarUInt64(pWritePointer, captureThreadId); - WriteVarUInt32(pWritePointer, captureProcNumber); - flags |= (1 << 1); - } - - if (m_lastHeader.ThreadId != instance.GetThreadId64()) - { - WriteVarUInt64(pWritePointer, instance.GetThreadId64()); - flags |= (1 << 2); - } - - if (m_lastHeader.StackId != stackId) - { - WriteVarUInt32(pWritePointer, stackId); - flags |= (1 << 3); - } - - const LARGE_INTEGER* timeStamp = instance.GetTimeStamp(); - WriteVarUInt64(pWritePointer, timeStamp->QuadPart - m_lastHeader.TimeStamp.QuadPart); - - if (memcmp(&m_lastHeader.ActivityId, instance.GetActivityId(), sizeof(GUID)) != 0) - { - memcpy(pWritePointer, instance.GetActivityId(), sizeof(GUID)); - pWritePointer += sizeof(GUID); - flags |= (1 << 4); - } - - if (memcmp(&m_lastHeader.RelatedActivityId, instance.GetRelatedActivityId(), sizeof(GUID)) != 0) - { - memcpy(pWritePointer, instance.GetRelatedActivityId(), sizeof(GUID)); - pWritePointer += sizeof(GUID); - flags |= (1 << 5); - } - - dataLength = instance.GetDataLength(); - if (m_lastHeader.DataLength != dataLength) - { - WriteVarUInt32(pWritePointer, dataLength); - flags |= (1 << 7); - } - - unsigned int bytesWritten = (unsigned int)(pWritePointer - m_compressedHeader); - unsigned int totalSize = 1 + bytesWritten + dataLength; - if (m_pWritePointer + totalSize >= m_pEndOfTheBuffer) - { - return false; - } - - m_lastHeader.MetadataId = instance.GetMetadataId(); - m_lastHeader.SequenceNumber = sequenceNumber; - m_lastHeader.ThreadId = instance.GetThreadId64(); - m_lastHeader.CaptureThreadId = captureThreadId; - m_lastHeader.CaptureProcNumber = captureProcNumber; - m_lastHeader.StackId = stackId; - m_lastHeader.TimeStamp.QuadPart = timeStamp->QuadPart; - memcpy(&m_lastHeader.ActivityId, instance.GetActivityId(), sizeof(GUID)); - memcpy(&m_lastHeader.RelatedActivityId, instance.GetRelatedActivityId(), sizeof(GUID)); - m_lastHeader.DataLength = dataLength; - - alignedEnd = m_pWritePointer + totalSize; - *m_pWritePointer = flags; - m_pWritePointer++; - memcpy(m_pWritePointer, m_compressedHeader, bytesWritten); - m_pWritePointer += bytesWritten; - } - - if (dataLength > 0) - { - memcpy(m_pWritePointer, instance.GetData(), dataLength); - m_pWritePointer += dataLength; - } - - if (m_format == EventPipeSerializationFormat::NetPerfV3) - { - unsigned int stackSize = instance.GetStackSize(); - memcpy(m_pWritePointer, &stackSize, sizeof(stackSize)); - m_pWritePointer += sizeof(stackSize); - - if (stackSize > 0) - { - memcpy(m_pWritePointer, instance.GetStack(), stackSize); - m_pWritePointer += stackSize; - } - } - - while (m_pWritePointer < alignedEnd) - { - *m_pWritePointer++ = (BYTE)0; // put padding at the end to get 4 bytes alignment of the payload - } - _ASSERTE(m_pWritePointer == alignedEnd); - - if (m_minTimeStamp.QuadPart > instance.GetTimeStamp()->QuadPart) - { - m_minTimeStamp.QuadPart = instance.GetTimeStamp()->QuadPart; - } - if (m_maxTimeStamp.QuadPart < instance.GetTimeStamp()->QuadPart) - { - m_maxTimeStamp.QuadPart = instance.GetTimeStamp()->QuadPart; - } - - return true; -} - -EventPipeEventBlock::EventPipeEventBlock(unsigned int maxBlockSize, EventPipeSerializationFormat format) : - EventPipeEventBlockBase(maxBlockSize, format, format >= EventPipeSerializationFormat::NetTraceV4) -{} - - -EventPipeMetadataBlock::EventPipeMetadataBlock(unsigned int maxBlockSize) : - EventPipeEventBlockBase(maxBlockSize, EventPipeSerializationFormat::NetTraceV4) -{} - -unsigned int GetSequencePointBlockSize(EventPipeSequencePoint* pSequencePoint) -{ - const unsigned int sizeOfSequenceNumber = - sizeof(ULONGLONG) + // thread id - sizeof(unsigned int); // sequence number - return sizeof(pSequencePoint->TimeStamp) + - sizeof(unsigned int) + // thread count - pSequencePoint->ThreadSequenceNumbers.GetCount() * sizeOfSequenceNumber; -} - -EventPipeSequencePointBlock::EventPipeSequencePointBlock(EventPipeSequencePoint* pSequencePoint) : - EventPipeBlock(GetSequencePointBlockSize(pSequencePoint)) -{ - const LARGE_INTEGER timeStamp = pSequencePoint->TimeStamp; - memcpy(m_pWritePointer, &timeStamp, sizeof(timeStamp)); - m_pWritePointer += sizeof(timeStamp); - - const unsigned int threadCount = pSequencePoint->ThreadSequenceNumbers.GetCount(); - memcpy(m_pWritePointer, &threadCount, sizeof(threadCount)); - m_pWritePointer += sizeof(threadCount); - - for (ThreadSequenceNumberMap::Iterator pCur = pSequencePoint->ThreadSequenceNumbers.Begin(); - pCur != pSequencePoint->ThreadSequenceNumbers.End(); - pCur++) - { - const ULONGLONG threadId = pCur->Key()->GetThread()->GetOSThreadId(); - memcpy(m_pWritePointer, &threadId, sizeof(threadId)); - m_pWritePointer += sizeof(threadId); - - const unsigned int sequenceNumber = pCur->Value(); - memcpy(m_pWritePointer, &sequenceNumber, sizeof(sequenceNumber)); - m_pWritePointer += sizeof(sequenceNumber); - } -} - -EventPipeStackBlock::EventPipeStackBlock(unsigned int maxBlockSize) : - EventPipeBlock(maxBlockSize) -{ - Clear(); -} - -void EventPipeStackBlock::Clear() -{ - m_hasInitialIndex = false; - m_initialIndex = 0; - m_count = 0; - EventPipeBlock::Clear(); -} - -bool EventPipeStackBlock::WriteStack(DWORD stackId, StackContents* pStack) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pBlock == NULL) - { - return false; - } - - unsigned int stackSize = pStack->GetSize(); - unsigned int totalSize = sizeof(stackSize) + stackSize; - if (m_pWritePointer + totalSize >= m_pEndOfTheBuffer) - { - return false; - } - - if (!m_hasInitialIndex) - { - m_hasInitialIndex = true; - m_initialIndex = stackId; - } - m_count++; - - memcpy(m_pWritePointer, &stackSize, sizeof(stackSize)); - m_pWritePointer += sizeof(stackSize); - - if (stackSize > 0) - { - memcpy(m_pWritePointer, pStack->GetPointer(), stackSize); - m_pWritePointer += stackSize; - } - - return true; -} -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeblock.h b/src/coreclr/vm/eventpipeblock.h deleted file mode 100644 index 9a3de96114b09..0000000000000 --- a/src/coreclr/vm/eventpipeblock.h +++ /dev/null @@ -1,236 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_BLOCK_H__ -#define __EVENTPIPE_BLOCK_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipeeventinstance.h" -#include "fastserializableobject.h" -#include "fastserializer.h" - -struct EventPipeSequencePoint; - -// The base class for all file blocks in the Nettrace file format -// This class handles memory management to buffer the block data, -// bookkeeping, block version numbers, and serializing the data -// to the file with correct alignment. -// Sub-classes decide the format of the block contents and how -// the blocks are named. -class EventPipeBlock : public FastSerializableObject -{ -public: - EventPipeBlock(unsigned int maxBlockSize, EventPipeSerializationFormat format = EventPipeSerializationFormat::NetTraceV4); - ~EventPipeBlock(); - - virtual void Clear(); - - unsigned int GetBytesWritten() const - { - return m_pBlock == nullptr ? 0 : (unsigned int)(m_pWritePointer - m_pBlock); - } - - // The size of the header for this block, if any - virtual unsigned int GetHeaderSize() - { - return 0; - } - - // Write the header to the stream - virtual void SerializeHeader(FastSerializer *pSerializer) - { - } - - void FastSerialize(FastSerializer *pSerializer) override - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pSerializer != NULL); - } - CONTRACTL_END; - - if (m_pBlock == NULL) - return; - - unsigned int dataSize = GetBytesWritten(); - // We shouldn't attempt to write blocks that have no data - _ASSERTE(dataSize != 0); - unsigned int headerSize = GetHeaderSize(); - unsigned int totalSize = dataSize + headerSize; - pSerializer->WriteBuffer((BYTE *)&totalSize, sizeof(totalSize)); - - unsigned int requiredPadding = pSerializer->GetRequiredPadding(); - if (requiredPadding != 0) - { - BYTE maxPadding[ALIGNMENT_SIZE - 1] = {}; // it's longest possible padding, we are going to use only part of it - pSerializer->WriteBuffer(maxPadding, requiredPadding); // we write zeros here, the reader is going to always read from the first aligned address of the serialized content - - _ASSERTE(pSerializer->HasWriteErrors() || (pSerializer->GetRequiredPadding() == 0)); - } - - SerializeHeader(pSerializer); - pSerializer->WriteBuffer(m_pBlock, dataSize); - } - -protected: - BYTE *m_pBlock; - BYTE *m_pWritePointer; - BYTE *m_pEndOfTheBuffer; - EventPipeSerializationFormat m_format; - - unsigned int GetSize() const - { - LIMITED_METHOD_CONTRACT; - return m_pBlock == nullptr ? 0 : (unsigned int)(m_pEndOfTheBuffer - m_pBlock); - } -}; - -struct EventPipeEventHeader -{ - DWORD MetadataId; - DWORD SequenceNumber; - ULONGLONG ThreadId; - ULONGLONG CaptureThreadId; - DWORD CaptureProcNumber; - DWORD StackId; - LARGE_INTEGER TimeStamp; - GUID ActivityId; - GUID RelatedActivityId; - DWORD DataLength; -}; - -// The base type for blocks that contain events (EventBlock and EventMetadataBlock) -class EventPipeEventBlockBase : public EventPipeBlock -{ -public: - EventPipeEventBlockBase(unsigned int maxBlockSize, EventPipeSerializationFormat format, bool fUseHeaderCompression = true); - - void Clear() override; - - unsigned int GetHeaderSize() override - { - if(m_format == EventPipeSerializationFormat::NetPerfV3) - { - return 0; - } - else - { - return sizeof(unsigned short) + // header size - sizeof(unsigned short) + // flags - sizeof(LARGE_INTEGER) + // min timestamp - sizeof(LARGE_INTEGER); // max timestamp - } - } - - void SerializeHeader(FastSerializer* pSerializer) override - { - if(m_format == EventPipeSerializationFormat::NetPerfV3) - { - return; - } - else - { - const unsigned short headerSize = GetHeaderSize(); - pSerializer->WriteBuffer((BYTE *)&headerSize, sizeof(headerSize)); - const unsigned short flags = m_fUseHeaderCompression ? 1 : 0; - pSerializer->WriteBuffer((BYTE *)&flags, sizeof(flags)); - pSerializer->WriteBuffer((BYTE *)&m_minTimeStamp, sizeof(m_minTimeStamp)); - pSerializer->WriteBuffer((BYTE *)&m_maxTimeStamp, sizeof(m_maxTimeStamp)); - } - } - - // Write an event to the block. - // Returns: - // - true: The write succeeded. - // - false: The write failed. In this case, the block should be considered full. - bool WriteEvent(EventPipeEventInstance &instance, ULONGLONG captureThreadId, unsigned int sequenceNumber, DWORD stackId, BOOL isSortedEvent); - -private: - EventPipeEventHeader m_lastHeader; - BYTE m_compressedHeader[100]; - bool m_fUseHeaderCompression; - LARGE_INTEGER m_minTimeStamp; - LARGE_INTEGER m_maxTimeStamp; -}; - -class EventPipeEventBlock : public EventPipeEventBlockBase -{ -public: - EventPipeEventBlock(unsigned int maxBlockSize, EventPipeSerializationFormat format); - - const char *GetTypeName() override - { - LIMITED_METHOD_CONTRACT; - return "EventBlock"; - } -}; - -class EventPipeMetadataBlock : public EventPipeEventBlockBase -{ -public: - EventPipeMetadataBlock(unsigned int maxBlockSize); - - const char *GetTypeName() override - { - LIMITED_METHOD_CONTRACT; - return "MetadataBlock"; - } -}; - -class EventPipeSequencePointBlock : public EventPipeBlock -{ -public: - EventPipeSequencePointBlock(EventPipeSequencePoint* sequencePoint); - - const char *GetTypeName() override - { - LIMITED_METHOD_CONTRACT; - return "SPBlock"; - } -}; - -// The block that contains interned stacks -class EventPipeStackBlock : public EventPipeBlock -{ -public: - EventPipeStackBlock(unsigned int maxBlockSize); - - unsigned int GetHeaderSize() override - { - return sizeof(unsigned int) + // start index - sizeof(unsigned int); // count of indices - } - - void SerializeHeader(FastSerializer* pSerializer) override - { - pSerializer->WriteBuffer((BYTE *)&m_initialIndex, sizeof(m_initialIndex)); - pSerializer->WriteBuffer((BYTE *)&m_count, sizeof(m_count)); - } - - void Clear() override; - - // Write a stack to the block - // Returns: - // - true: The write succeeded. - // - false: The write failed. In this case, the block should be considered full. - bool WriteStack(DWORD stackId, StackContents* pStack); - - const char *GetTypeName() override - { - LIMITED_METHOD_CONTRACT; - return "StackBlock"; - } - -private: - bool m_hasInitialIndex; - unsigned int m_initialIndex; - unsigned int m_count; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_BLOCK_H__ diff --git a/src/coreclr/vm/eventpipebuffer.cpp b/src/coreclr/vm/eventpipebuffer.cpp deleted file mode 100644 index 50f4076ee2337..0000000000000 --- a/src/coreclr/vm/eventpipebuffer.cpp +++ /dev/null @@ -1,310 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipeeventinstance.h" -#include "eventpipeeventpayload.h" -#include "eventpipebuffer.h" -#include "eventpipebuffermanager.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize, EventPipeThread* pWriterThread, unsigned int eventSequenceNumber) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - m_state = EventPipeBufferState::WRITABLE; - m_pWriterThread = pWriterThread; - m_eventSequenceNumber = eventSequenceNumber; - // Use ClrVirtualAlloc instead of malloc to allocate buffer to avoid potential internal fragmentation in the native CRT heap. - // (See https://github.com/dotnet/runtime/pull/35924 and https://github.com/microsoft/ApplicationInsights-dotnet/issues/1678 for more details) - // - // This fix does cause a little bit of performance regression (1-2%) in throughput, - // but within acceptable boundaries, while minimizing the risk of the fix to be backported - // to servicing releases. We may come back in the future to reassess this and potentially improve - // the throughput via more performant solution afterwards. - m_pBuffer = (BYTE*)ClrVirtualAlloc(NULL, bufferSize, MEM_COMMIT, PAGE_READWRITE); - m_pLimit = m_pBuffer + bufferSize; - m_pCurrent = GetNextAlignedAddress(m_pBuffer); - - QueryPerformanceCounter(&m_creationTimeStamp); - _ASSERTE(m_creationTimeStamp.QuadPart > 0); - m_pCurrentReadEvent = NULL; - m_pPrevBuffer = NULL; - m_pNextBuffer = NULL; -} - -EventPipeBuffer::~EventPipeBuffer() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - // We should never be deleting a buffer that a writer thread might still try to write to - PRECONDITION(m_state == EventPipeBufferState::READ_ONLY); - } - CONTRACTL_END; - - ClrVirtualFree(m_pBuffer, 0, MEM_RELEASE); -} - -bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(((size_t)m_pCurrent % AlignmentSize) == 0); - // We should never try to write to a buffer that isn't expecting to be written to. - PRECONDITION(m_state == EventPipeBufferState::WRITABLE); - } - CONTRACTL_END; - - - // Calculate the size of the event. - unsigned int eventSize = sizeof(EventPipeEventInstance) + payload.GetSize(); - - // Make sure we have enough space to write the event. - if(m_pCurrent + eventSize > m_pLimit) - return false; - - bool success = true; - EX_TRY - { - // Calculate the location of the data payload. - BYTE *pDataDest = payload.GetSize() == 0 ? NULL : m_pCurrent + sizeof(EventPipeEventInstance); - - // Placement-new the EventPipeEventInstance. - // if pthread is NULL, it's likely we are running in something like a GC thread which is not a Thread object, so it can't have an activity ID set anyway - - StackContents s; - memset((void *)&s, 0, sizeof(s)); - if (event.NeedStack() && !session.RundownEnabled() && pStack == NULL) - { - EventPipe::WalkManagedStackForCurrentThread(s); - pStack = &s; - } - - unsigned int procNumber = EventPipe::GetCurrentProcessorNumber(); - EventPipeEventInstance *pInstance = new (m_pCurrent) EventPipeEventInstance( - event, - procNumber, - (pThread == NULL) ? -#ifdef TARGET_UNIX - ::PAL_GetCurrentOSThreadId() -#else - ::GetCurrentThreadId() -#endif - : pThread->GetOSThreadId64(), - pDataDest, - payload.GetSize(), - (pThread == NULL) ? NULL : pActivityId, - pRelatedActivityId); - - // Copy the stack if a separate stack trace was provided. - if (pStack != NULL) - { - StackContents *pInstanceStack = pInstance->GetStack(); - pStack->CopyTo(pInstanceStack); - } - - // Write the event payload data to the buffer. - if (payload.GetSize() > 0) - { - payload.CopyData(pDataDest); - } - } - EX_CATCH - { - // If a failure occurs, bail out and don't advance the pointer. - success = false; - } - EX_END_CATCH(SwallowAllExceptions); - - if (success) - { - // Advance the current pointer past the event. - m_pCurrent = GetNextAlignedAddress(m_pCurrent + eventSize); - } - - return success; -} - -LARGE_INTEGER EventPipeBuffer::GetCreationTimeStamp() const -{ - LIMITED_METHOD_CONTRACT; - - return m_creationTimeStamp; -} - -void EventPipeBuffer::MoveNextReadEvent() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_state == EventPipeBufferState::READ_ONLY); - } - CONTRACTL_END; - - EventPipeEventInstance *pNextInstance = NULL; - - // If m_pCurrentReadEvent is NULL we've reached the end of the events - if (m_pCurrentReadEvent != NULL) - { - // Confirm that pEvent is within the used range of the buffer. - if (((BYTE*)m_pCurrentReadEvent < m_pBuffer) || ((BYTE*)m_pCurrentReadEvent >= m_pCurrent)) - { - _ASSERT(!"Input pointer is out of range."); - m_pCurrentReadEvent = NULL; - } - else - { - if (m_pCurrentReadEvent->GetData()) - { - // We have a pointer within the bounds of the buffer. - // Find the next event by skipping the current event with it's data payload immediately after the instance. - m_pCurrentReadEvent = (EventPipeEventInstance *)GetNextAlignedAddress(const_cast(m_pCurrentReadEvent->GetData() + m_pCurrentReadEvent->GetDataLength())); - } - else - { - // In case we do not have a payload, the next instance is right after the current instance - m_pCurrentReadEvent = (EventPipeEventInstance*)GetNextAlignedAddress((BYTE*)(m_pCurrentReadEvent + 1)); - } - // this may roll over and that is fine - m_eventSequenceNumber++; - - // Check to see if we've reached the end of the written portion of the buffer. - if ((BYTE*)m_pCurrentReadEvent >= m_pCurrent) - { - m_pCurrentReadEvent = NULL; - } - } - } - - // Ensure that the timestamp is valid. The buffer is zero'd before use, so a zero timestamp is invalid. -#ifdef DEBUG - if (m_pCurrentReadEvent != NULL) - { - LARGE_INTEGER nextTimeStamp = *m_pCurrentReadEvent->GetTimeStamp(); - _ASSERTE(nextTimeStamp.QuadPart != 0); - } -#endif -} - -EventPipeEventInstance* EventPipeBuffer::GetCurrentReadEvent() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_state == READ_ONLY); - } - CONTRACTL_END; - - return m_pCurrentReadEvent; -} - -unsigned int EventPipeBuffer::GetCurrentSequenceNumber() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_state == READ_ONLY); - } - CONTRACTL_END; - - return m_eventSequenceNumber; -} - -EventPipeThread* EventPipeBuffer::GetWriterThread() -{ - LIMITED_METHOD_CONTRACT; - return m_pWriterThread; -} - -EventPipeBufferState EventPipeBuffer::GetVolatileState() -{ - LIMITED_METHOD_CONTRACT; - return m_state.Load(); -} - -void EventPipeBuffer::ConvertToReadOnly() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pWriterThread->GetLock()->OwnedByCurrentThread()); - _ASSERTE(m_pCurrentReadEvent == NULL); - m_state.Store(EventPipeBufferState::READ_ONLY); - - // If this buffer contains an event, select it. - BYTE *pFirstAlignedInstance = GetNextAlignedAddress(m_pBuffer); - if (m_pCurrent > pFirstAlignedInstance) - { - m_pCurrentReadEvent = (EventPipeEventInstance*)pFirstAlignedInstance; - } - else - { - m_pCurrentReadEvent = NULL; - } -} - -#ifdef _DEBUG -bool EventPipeBuffer::EnsureConsistency() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Check to see if the buffer is empty. - if (GetNextAlignedAddress(m_pBuffer) == m_pCurrent) - { - // Make sure that the buffer size is greater than zero. - _ASSERTE(m_pBuffer != m_pLimit); - } - - // Validate the contents of the filled portion of the buffer. - BYTE *ptr = GetNextAlignedAddress(m_pBuffer); - while (ptr < m_pCurrent) - { - // Validate the event. - EventPipeEventInstance *pInstance = (EventPipeEventInstance *)ptr; - _ASSERTE(pInstance->EnsureConsistency()); - - // Validate that payload and length match. - _ASSERTE((pInstance->GetData() != NULL && pInstance->GetDataLength() > 0) || (pInstance->GetData() == NULL && pInstance->GetDataLength() == 0)); - - // Skip the event. - ptr = GetNextAlignedAddress(ptr + sizeof(*pInstance) + pInstance->GetDataLength()); - } - - // When we're done walking the filled portion of the buffer, - // ptr should be the same as m_pCurrent. - _ASSERTE(ptr == m_pCurrent); - - // Walk the rest of the buffer, making sure it is properly zeroed. - while (ptr < m_pLimit) - { - _ASSERTE(*ptr++ == 0); - } - - return true; -} -#endif // _DEBUG - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipebuffer.h b/src/coreclr/vm/eventpipebuffer.h deleted file mode 100644 index 4d2eaf8f9bcef..0000000000000 --- a/src/coreclr/vm/eventpipebuffer.h +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_BUFFER_H__ -#define __EVENTPIPE_BUFFER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipeevent.h" -#include "eventpipeeventinstance.h" -#include "eventpipesession.h" - -class EventPipeThread; - - -// Synchronization -// -// EventPipeBuffer starts off writable and accumulates events in a buffer, then at some point converts to be readable and a second thread can -// read back the events which have accumulated. The transition occurs when calling ConvertToReadOnly(). Write methods will assert if the buffer -// isn't writable and read-related methods will assert if it isn't readable. Methods that have no asserts should have immutable results that -// can be used at any point during the buffer's lifetime. The buffer has no internal locks so it is the caller's responsibility to synchronize -// their usage. -// Writing into the buffer and calling ConvertToReadOnly() is always done with EventPipeThread::m_lock held. The eventual reader thread can do -// a few different things to ensure it sees a consistent state: -// 1) Take the writer's EventPipeThread::m_lock at least once after the last time the writer writes events -// 2) Use a memory barrier that prevents reader loads from being re-ordered earlier, such as the one that will occur implicitly by evaluating -// EventPipeBuffer::GetVolatileState() - - -enum EventPipeBufferState -{ - // This buffer is currently assigned to a thread and pWriterThread may write events into it - // at any time - WRITABLE = 0, - - // This buffer has been returned to the EventPipeBufferManager and pWriterThread is guaranteed - // to never access it again. - READ_ONLY = 1 -}; - -class EventPipeBuffer -{ - - friend class EventPipeBufferList; - friend class EventPipeBufferManager; - -private: - - // Instances of EventPipeEventInstance in the buffer must be 8-byte aligned. - // It is OK for the data payloads to be unaligned because they are opaque blobs that are copied via memcpy. - const size_t AlignmentSize = 8; - - // State transition WRITABLE -> READ_ONLY only occurs while holding the m_pWriterThread->m_lock; - // It can be read at any time - Volatile m_state; - - // Thread that is/was allowed to write into this buffer when m_state == WRITABLE - EventPipeThread* m_pWriterThread; - - // The sequence number corresponding to m_pCurrentReadEvent - // Prior to read iteration it is the sequence number of the first event in the buffer - unsigned int m_eventSequenceNumber; - - // A pointer to the actual buffer. - BYTE *m_pBuffer; - - // The current write pointer. - BYTE *m_pCurrent; - - // The max write pointer (end of the buffer). - BYTE *m_pLimit; - - // The timestamp the buffer was created. If our clock source - // is monotonic then all events in the buffer should have - // timestamp >= this one. If not then all bets are off. - LARGE_INTEGER m_creationTimeStamp; - - // Pointer to the current event being read - EventPipeEventInstance *m_pCurrentReadEvent; - - // Each buffer will become part of a per-thread linked list of buffers. - // The linked list is invasive, thus we declare the pointers here. - EventPipeBuffer *m_pPrevBuffer; - EventPipeBuffer *m_pNextBuffer; - - unsigned int GetSize() const - { - LIMITED_METHOD_CONTRACT; - return (unsigned int)(m_pLimit - m_pBuffer); - } - - EventPipeBuffer* GetPrevious() const - { - LIMITED_METHOD_CONTRACT; - return m_pPrevBuffer; - } - - EventPipeBuffer* GetNext() const - { - LIMITED_METHOD_CONTRACT; - return m_pNextBuffer; - } - - void SetPrevious(EventPipeBuffer *pBuffer) - { - LIMITED_METHOD_CONTRACT; - m_pPrevBuffer = pBuffer; - } - - void SetNext(EventPipeBuffer *pBuffer) - { - LIMITED_METHOD_CONTRACT; - m_pNextBuffer = pBuffer; - } - - FORCEINLINE BYTE* GetNextAlignedAddress(BYTE *pAddress) - { - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pBuffer <= pAddress && pAddress <= m_pLimit); - - pAddress = (BYTE*)ALIGN_UP(pAddress, AlignmentSize); - - _ASSERTE((size_t)pAddress % AlignmentSize == 0); - return pAddress; - } - -public: - - EventPipeBuffer(unsigned int bufferSize, EventPipeThread* pWriterThread, unsigned int eventSequenceNumber); - ~EventPipeBuffer(); - - // Write an event to the buffer. - // An optional stack trace can be provided for sample profiler events. - // Otherwise, if a stack trace is needed, one will be automatically collected. - // Returns: - // - true: The write succeeded. - // - false: The write failed. In this case, the buffer should be considered full. - bool WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL); - - // Get the timestamp the buffer was created. - LARGE_INTEGER GetCreationTimeStamp() const; - - // Advances read cursor to the next event or NULL if there aren't any more. When the - // buffer is first made readable the cursor is automatically positioned on the first - // event or NULL if there are no events in the buffer. - void MoveNextReadEvent(); - - // Returns the event at the current read cursor. The returned event pointer is valid - // until the buffer is deleted. - EventPipeEventInstance* GetCurrentReadEvent(); - - // Gets the sequence number of the event corresponding to GetCurrentReadEvent(); - unsigned int GetCurrentSequenceNumber(); - - // Get the thread that is (or was) assigned to write to this buffer - EventPipeThread* GetWriterThread(); - - // Check the state of the buffer - EventPipeBufferState GetVolatileState(); - - // Convert the buffer writable to readable - void ConvertToReadOnly(); - -#ifdef _DEBUG - bool EnsureConsistency(); -#endif // _DEBUG -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_BUFFER_H__ diff --git a/src/coreclr/vm/eventpipebuffermanager.cpp b/src/coreclr/vm/eventpipebuffermanager.cpp deleted file mode 100644 index d55ec54a8fab9..0000000000000 --- a/src/coreclr/vm/eventpipebuffermanager.cpp +++ /dev/null @@ -1,1303 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipeconfiguration.h" -#include "eventpipebuffer.h" -#include "eventpipebuffermanager.h" -#include "eventpipeeventpayload.h" -#include "eventpipefile.h" -#include "eventpipethread.h" -#include "eventpipesession.h" - - -#ifdef FEATURE_PERFTRACING - -template -T Clamp(T min, T value, T max) -{ - STATIC_CONTRACT_LEAF; - return Min(Max(min, value), max); -} - -EventPipeBufferManager::EventPipeBufferManager(EventPipeSession* pSession, size_t maxSizeOfAllBuffers, size_t sequencePointAllocationBudget) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_pSession = pSession; - m_pThreadSessionStateList = new SList>(); - m_sizeOfAllBuffers = 0; - m_lock.Init(LOCK_TYPE_DEFAULT); - m_waitEvent.CreateAutoEvent(TRUE); - -#ifdef _DEBUG - m_numBuffersAllocated = 0; - m_numBuffersStolen = 0; - m_numBuffersLeaked = 0; - m_numEventsStored = 0; - m_numEventsDropped = 0; - m_numEventsWritten = 0; -#endif // _DEBUG - - m_pCurrentEvent = nullptr; - m_pCurrentBuffer = nullptr; - m_pCurrentBufferList = nullptr; - - m_maxSizeOfAllBuffers = Clamp((size_t)100 * 1024, maxSizeOfAllBuffers, (size_t)UINT32_MAX); - - if (sequencePointAllocationBudget == 0) - { - // sequence points disabled - m_sequencePointAllocationBudget = 0; - m_remainingSequencePointAllocationBudget = 0; - } - else - { - m_sequencePointAllocationBudget = Clamp((size_t)1024 * 1024, sequencePointAllocationBudget, (size_t)1024 * 1024 * 1024); - m_remainingSequencePointAllocationBudget = m_sequencePointAllocationBudget; - } - m_sequencePoints.Init(); -} - -EventPipeBufferManager::~EventPipeBufferManager() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - DeAllocateBuffers(); -} - -#ifdef DEBUG -bool EventPipeBufferManager::IsLockOwnedByCurrentThread() -{ - return m_lock.OwnedByCurrentThread(); -} -#endif - -EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(EventPipeThreadSessionState* pSessionState, - unsigned int requestSize, - BOOL & writeSuspended) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pSessionState != NULL); - PRECONDITION(requestSize > 0); - } - CONTRACTL_END; - - // Allocating a buffer requires us to take the lock. - SpinLockHolder _slh(&m_lock); - - bool allocateNewBuffer = false; - - EventPipeBufferList *pThreadBufferList = pSessionState->GetBufferList(); - if (pThreadBufferList == NULL) - { - pThreadBufferList = new (nothrow) EventPipeBufferList(this, pSessionState->GetThread()); - if (pThreadBufferList == NULL) - { - return NULL; - } - - SListElem *pElem = new (nothrow) SListElem(pSessionState); - if (pElem == NULL) - { - delete pThreadBufferList; - return NULL; - } - - m_pThreadSessionStateList->InsertTail(pElem); - pSessionState->SetBufferList(pThreadBufferList); - } - - // Determine if policy allows us to allocate another buffer - size_t availableBufferSize = m_maxSizeOfAllBuffers - m_sizeOfAllBuffers; - if (requestSize <= availableBufferSize) - { - allocateNewBuffer = true; - } - - EventPipeBuffer *pNewBuffer = NULL; - if (allocateNewBuffer) - { - // Pick a buffer size by multiplying the base buffer size by the number of buffers already allocated for this thread. - unsigned int sizeMultiplier = pThreadBufferList->GetCount() + 1; - - // Pick the base buffer size based. Debug builds have a smaller size to stress the allocate/steal path more. - unsigned int baseBufferSize = -#ifdef _DEBUG - 30 * 1024; // 30K -#else - 100 * 1024; // 100K -#endif - unsigned int bufferSize = baseBufferSize * sizeMultiplier; - - // Make sure that buffer size >= request size so that the buffer size does not - // determine the max event size. - _ASSERTE(requestSize <= availableBufferSize); - bufferSize = Max(requestSize, bufferSize); - bufferSize = Min((unsigned int)bufferSize, (unsigned int)availableBufferSize); - - // Don't allow the buffer size to exceed 1MB. - const unsigned int maxBufferSize = 1024 * 1024; - bufferSize = Min(bufferSize, maxBufferSize); - - // Make the buffer size fit into with pagesize-aligned block, since ClrVirtualAlloc expects page-aligned sizes to be passed as arguments (see ctor of EventPipeBuffer) - bufferSize = (bufferSize + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast(g_SystemInfo.dwAllocationGranularity - 1); - - // EX_TRY is used here as opposed to new (nothrow) because - // the constructor also allocates a private buffer, which - // could throw, and cannot be easily checked - EX_TRY - { - // The sequence counter is exclusively mutated on this thread so this is a thread-local - // read. - unsigned int sequenceNumber = pSessionState->GetVolatileSequenceNumber(); - pNewBuffer = new EventPipeBuffer(bufferSize, pSessionState->GetThread(), sequenceNumber); - } - EX_CATCH - { - pNewBuffer = NULL; - } - EX_END_CATCH(SwallowAllExceptions); - - if (pNewBuffer == NULL) - { - return NULL; - } - - m_sizeOfAllBuffers += bufferSize; - if (m_sequencePointAllocationBudget != 0) - { - // sequence point bookkeeping - if (bufferSize >= m_remainingSequencePointAllocationBudget) - { - EventPipeSequencePoint* pSequencePoint = new (nothrow) EventPipeSequencePoint(); - if (pSequencePoint != NULL) - { - InitSequencePointThreadListHaveLock(pSequencePoint); - EnqueueSequencePoint(pSequencePoint); - } - m_remainingSequencePointAllocationBudget = m_sequencePointAllocationBudget; - } - else - { - m_remainingSequencePointAllocationBudget -= bufferSize; - } - } -#ifdef _DEBUG - m_numBuffersAllocated++; -#endif // _DEBUG - } - - // Set the buffer on the thread. - if (pNewBuffer != NULL) - { - pThreadBufferList->InsertTail(pNewBuffer); - return pNewBuffer; - } - - return NULL; -} - -void EventPipeBufferManager::EnqueueSequencePoint(EventPipeSequencePoint* pSequencePoint) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - m_sequencePoints.InsertTail(pSequencePoint); -} - -void EventPipeBufferManager::InitSequencePointThreadList(EventPipeSequencePoint* pSequencePoint) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(!IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - SpinLockHolder __slh(&m_lock); - InitSequencePointThreadListHaveLock(pSequencePoint); -} - -void EventPipeBufferManager::InitSequencePointThreadListHaveLock(EventPipeSequencePoint* pSequencePoint) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - EventPipeThreadSessionState* pSessionState = pElem->GetValue(); - - // The sequence number captured here is not guaranteed to be the most recent sequence number, nor - // is it guaranteed to match the number of events we would observe in the thread's write buffer - // memory. This is only used as a lower bound on the number of events the thread has attempted to - // write at the timestamp we will capture below. - // - // The sequence number is the value that will be used by the next event, so the last written - // event is one less. Sequence numbers are allowed to overflow, so going backwards is allowed to - // underflow. - unsigned int sequenceNumber = pSessionState->GetVolatileSequenceNumber() - 1; - EX_TRY - { - pSequencePoint->ThreadSequenceNumbers.Add(pSessionState, sequenceNumber); - pSessionState->GetThread()->AddRef(); - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions); - - pElem = m_pThreadSessionStateList->GetNext(pElem); - } - - // This needs to come after querying the thread sequence numbers to ensure that any recorded - // sequence number is <= the actual sequence number at this timestamp - PRECONDITION(m_lock.OwnedByCurrentThread()); - QueryPerformanceCounter(&pSequencePoint->TimeStamp); -} - -void EventPipeBufferManager::DequeueSequencePoint() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - delete m_sequencePoints.RemoveHead(); -} - -bool EventPipeBufferManager::TryPeekSequencePoint(EventPipeSequencePoint** ppSequencePoint) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - *ppSequencePoint = m_sequencePoints.GetHead(); - return *ppSequencePoint != NULL; -} - -void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (pBuffer != NULL) - { - m_sizeOfAllBuffers -= pBuffer->GetSize(); - delete (pBuffer); -#ifdef _DEBUG - m_numBuffersAllocated--; -#endif // _DEBUG - } -} - -bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - // The input thread must match the current thread because no lock is taken on the buffer. - PRECONDITION(pThread == GetThread()); - } - CONTRACTL_END; - - // Check to see an event thread was specified. If not, then use the current thread. - if (pEventThread == NULL) - { - pEventThread = pThread; - } - - // Before we pick a buffer, make sure the event is enabled. - if (!event.IsEnabled()) - { - return false; - } - - StackContents stackContents; - if (pStack == NULL && event.NeedStack() && !session.RundownEnabled()) - { - EventPipe::WalkManagedStackForCurrentThread(stackContents); - pStack = &stackContents; - } - - // See if the thread already has a buffer to try. - bool allocNewBuffer = false; - EventPipeBuffer *pBuffer = NULL; - - EventPipeThread *pEventPipeThread = EventPipeThread::Get(); - - if (pEventPipeThread == NULL) - { - return false; - } - - EventPipeThreadSessionState* pSessionState = NULL; - { - SpinLockHolder _slh(pEventPipeThread->GetLock()); - pSessionState = pEventPipeThread->GetOrCreateSessionState(m_pSession); - if (pSessionState == NULL) - { - return false; - } - pBuffer = pSessionState->GetWriteBuffer(); - if (pBuffer == NULL) - { - allocNewBuffer = true; - } - else - { - // Attempt to write the event to the buffer. If this fails, we should allocate a new buffer. - if (pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack)) - { - pSessionState->IncrementSequenceNumber(); - } - else - { - allocNewBuffer = true; - } - } - } - - // allocNewBuffer is reused below to detect if overflow happened, so cache it here to see if we should - // signal the reader thread - bool shouldSignalReaderThread = allocNewBuffer; - - // Check to see if we need to allocate a new buffer, and if so, do it here. - if (allocNewBuffer) - { - // We previously switched to preemptive mode here, however, this is not safe and can cause deadlocks. - // When a GC is started, and background threads are created (for the first BGC), a thread creation event is fired. - // When control gets here the buffer is allocated, but then the thread hangs waiting for the GC to complete - // (it was marked as started before creating threads) so that it can switch back to cooperative mode. - // However, the GC is waiting on this call to return so that it can make forward progress. Thus it is not safe - // to switch to preemptive mode here. - - unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize(); - BOOL writeSuspended = FALSE; - pBuffer = AllocateBufferForThread(pSessionState, requestSize, writeSuspended); - if (pBuffer == NULL) - { - // We treat this as the WriteEvent() call occurring after this session stopped listening for events, effectively the - // same as if event.IsEnabled() test above returned false. - if (writeSuspended) - return false; - - // This lock looks unnecessary for the sequence number, but didn't want to - // do a broader refactoring to take it out. If it shows up as a perf - // problem then we should. - SpinLockHolder _slh(pEventPipeThread->GetLock()); - pSessionState->IncrementSequenceNumber(); - } - else - { - pEventPipeThread = EventPipeThread::Get(); - _ASSERTE(pEventPipeThread != NULL); - { - SpinLockHolder _slh(pEventPipeThread->GetLock()); - - pSessionState->SetWriteBuffer(pBuffer); - - // Try to write the event after we allocated a buffer. - // This is the first time if the thread had no buffers before the call to this function. - // This is the second time if this thread did have one or more buffers, but they were full. - allocNewBuffer = !pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack); - _ASSERTE(!allocNewBuffer); - pSessionState->IncrementSequenceNumber(); - } - } - } - - if (shouldSignalReaderThread) - { - // Indicate that there is new data to be read - m_waitEvent.Set(); - } - -#ifdef _DEBUG - if (!allocNewBuffer) - { - InterlockedIncrement(&m_numEventsStored); - } - else - { - InterlockedIncrement(&m_numEventsDropped); - } -#endif // _DEBUG - return !allocNewBuffer; -} - -void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp, bool *eventsWritten) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pFile != nullptr); - PRECONDITION(GetCurrentEvent() == nullptr); - } - CONTRACTL_END; - - // The V4 format doesn't require full event sorting as V3 did - // See the comments in WriteAllBufferToFileV4 for more details - if (pFile->GetSerializationFormat() >= EventPipeSerializationFormat::NetTraceV4) - { - WriteAllBuffersToFileV4(pFile, stopTimeStamp, eventsWritten); - } - else - { - WriteAllBuffersToFileV3(pFile, stopTimeStamp, eventsWritten); - } -} - -void EventPipeBufferManager::WriteAllBuffersToFileV3(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp, bool *pEventsWritten) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pFile != nullptr); - PRECONDITION(GetCurrentEvent() == nullptr); - PRECONDITION(pEventsWritten != nullptr); - } - CONTRACTL_END; - - *pEventsWritten = false; - - // Naively walk the circular buffer, writing the event stream in timestamp order. - MoveNextEventAnyThread(stopTimeStamp); - while (GetCurrentEvent() != nullptr) - { - *pEventsWritten = true; - pFile->WriteEvent(*GetCurrentEvent(), /*CaptureThreadId=*/0, /*sequenceNumber=*/0, /*IsSorted=*/TRUE); - MoveNextEventAnyThread(stopTimeStamp); - } - pFile->Flush(); -} - -void EventPipeBufferManager::WriteAllBuffersToFileV4(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp, bool *pEventsWritten) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pFile != nullptr); - PRECONDITION(GetCurrentEvent() == nullptr); - PRECONDITION(pEventsWritten != nullptr); - } - CONTRACTL_END; - - // - // In V3 of the format this code does a full timestamp order sort on the events which made the file easier to consume, - // but the perf implications for emitting the file are less desirable. Imagine an application with 500 threads emitting - // 10 events per sec per thread (granted this is a questionable number of threads to use in an app, but that isn't - // under our control). A naive sort of 500 ordered lists is going to pull the oldest event from each of 500 lists, - // compare all the timestamps, then emit the oldest one. This could easily add a thousand CPU cycles per-event. A - // better implementation could maintain a min-heap so that we scale O(log(N)) instead of O(N)but fundamentally sorting - // has a cost and we didn't want a file format that forces the runtime to pay it on every event. - // - // We minimize sorting using two mechanisms: - // 1) Explicit sequence points - Every X MB of buffer space that is distributed to threads we record the current - // timestamp. We ensure when writing events in the file that all events before the sequence point time are written - // prior to the sequence point and all events with later timestamps are written afterwards. For example assume - // two threads emitted events like this(B_14 = event on thread B with timestamp 14): - // - // Time ---> - // Thread A events: A_1 A_4 A_9 A_10 A_11 A_12 A_13 A_15 - // Thread B events: B_2 B_6 B_14 B_20 - // /|\. - // | - // Assume sequence point was triggered here - // Then we promise that events A_1, A_4, A_9, A_10, B_2_ and B_6 will be written in one or more event blocks, - // (not necessarily in sorted order) then a sequence point block is written, then events A_11, A_12, A_13, B_14, - // A_15, and B_20 will be written. The reader can cache all the events between sequence points, sort them, and - // then emit them in a total order. Triggering sequence points based on buffer allocation ensures that we won't - // need an arbitrarily large cache in the reader to store all the events, however there is a fair amount of slop - // in the current scheme. In the worst case you could imagine N threads, each of which was already allocated a - // max size buffer (currently 1MB) but only an insignificant portion has been used. Even if the trigger - // threshhold is a modest amount such as 10MB, the threads could first write 1MB * N bytes to the stream - // beforehand. I'm betting on these extreme cases being very rare and even something like 1GB isn't an unreasonable - // amount of virtual memory to use on to parse an extreme trace. However if I am wrong we can control - // both the allocation policy and the triggering instrumentation. Nothing requires us to give out 1MB buffers to - // 1000 threads simultaneously, nor are we prevented from observing buffer usage at finer granularity than we - // allocated. - // - // 2) We mark which events are the oldest ones in the stream at the time we emit them and we do this at regular - // intervals of time. When we emit all the events every X ms, there will be at least one event in there with - // a marker showing that all events older than that one have already been emitted. As soon as the reader sees - // this it can sort the events which have older timestamps and emit them. - // - // Why have both mechanisms? The sequence points in #1 worked fine to guarantee that given the whole trace you - // could sort it with a bounded cache, but it doesn't help much for real-time usage. Imagine that we have two - // threads emitting 1KB/sec of events and sequence points occur every 10MB. The reader would need to wait for - // 10,000 seconds to accumulate all the events before it could sort and process them. On the other hand if we - // only had mechanism #2 the reader can generate the sort quickly in real-time, but it is messy to do the buffer - // management. The reader reads in a bunch of event block buffers and starts emitting events from sub-sections - // of each of them and needs to know when each buffer can be released. The explicit sequence point makes that - // very easy - every sequence point all buffers can be released and no further bookkeeping is required. - - *pEventsWritten = false; - - EventPipeSequencePoint* pSequencePoint; - LARGE_INTEGER curTimestampBoundary; - curTimestampBoundary.QuadPart = stopTimeStamp.QuadPart; - { - SpinLockHolder _slh(&m_lock); - if (TryPeekSequencePoint(&pSequencePoint)) - { - curTimestampBoundary.QuadPart = Min(curTimestampBoundary.QuadPart, pSequencePoint->TimeStamp.QuadPart); - } - } - - while(true) // loop across sequence points - { - while (true) // loop across events within a sequence point boundary - { - // pick the thread that has the oldest event - MoveNextEventAnyThread(curTimestampBoundary); - if (GetCurrentEvent() == nullptr) - { - break; - } - ULONGLONG captureThreadId = GetCurrentEventBuffer()->GetWriterThread()->GetOSThreadId(); - EventPipeBufferList* pBufferList = GetCurrentEventBufferList(); - - // loop across events on this thread - bool eventsWritten = false; - unsigned int sequenceNumber = 0; - while (GetCurrentEvent() != nullptr) - { - // The first event emitted on each thread (detected by !eventsWritten) is guaranteed to - // be the oldest event cached in our buffers so we mark it. This implements mechanism #2 - // in the big comment above. - - sequenceNumber = GetCurrentSequenceNumber(); - pFile->WriteEvent(*GetCurrentEvent(), captureThreadId, sequenceNumber, !eventsWritten); - eventsWritten = true; - MoveNextEventSameThread(curTimestampBoundary); - } - pBufferList->SetLastReadSequenceNumber(sequenceNumber); - // Have we written events in any sequence point? - *pEventsWritten = eventsWritten || *pEventsWritten; - } - - // This finishes any current partially filled EventPipeBlock, and flushes it to the stream - pFile->Flush(); - - // there are no more events prior to curTimestampBoundary - if (curTimestampBoundary.QuadPart == stopTimeStamp.QuadPart) - { - // We are done - break; - } - else // (curTimestampBoundary.QuadPart < stopTimeStamp.QuadPart) - { - // stopped at sequence point case - - // the sequence point captured a lower bound for sequence number on each thread, but iterating - // through the events we may have observed that a higher numbered event was recorded. If so we - // should adjust the sequence numbers upwards to ensure the data in the stream is consistent. - { - SpinLockHolder _slh(&m_lock); - - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - EventPipeThreadSessionState* pSessionState = pElem->GetValue(); - unsigned int threadSequenceNumber = 0; - pSequencePoint->ThreadSequenceNumbers.Lookup(pSessionState, &threadSequenceNumber); - unsigned int lastReadSequenceNumber = pSessionState->GetBufferList()->GetLastReadSequenceNumber(); - // Sequence numbers can overflow so we can't use a direct lastRead > sequenceNumber comparison - // If a thread is able to drop more than 0x80000000 events in between sequence points then we will - // miscategorize it, but that seems unlikely. - unsigned int lastReadDelta = lastReadSequenceNumber - threadSequenceNumber; - if (0 < lastReadDelta && lastReadDelta < 0x80000000) - { - pSequencePoint->ThreadSequenceNumbers.AddOrReplace(ThreadSequenceNumberMap::element_t(pSessionState, lastReadSequenceNumber)); - } - pElem = m_pThreadSessionStateList->GetNext(pElem); - } - } - - // emit the sequence point into the file - pFile->WriteSequencePoint(pSequencePoint); - - // move to the next sequence point if any - { - SpinLockHolder _slh(&m_lock); - - // advance to the next sequence point, if any - DequeueSequencePoint(); - curTimestampBoundary.QuadPart = stopTimeStamp.QuadPart; - if (TryPeekSequencePoint(&pSequencePoint)) - { - curTimestampBoundary.QuadPart = Min(curTimestampBoundary.QuadPart, pSequencePoint->TimeStamp.QuadPart); - } - } - } - } -} - -EventPipeEventInstance* EventPipeBufferManager::GetNextEvent() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(!EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // PERF: This may be too aggressive? If this method is being called frequently enough to keep pace with the - // writing threads we could be in a state of high lock contention and lots of churning buffers. Each writer - // would take several locks, allocate a new buffer, write one event into it, then the reader would take the - // lock, convert the buffer to read-only and read the single event out of it. Allowing more events to accumulate - // in the buffers before converting between writable and read-only amortizes a lot of the overhead. One way - // to achieve that would be picking a stopTimeStamp that was Xms in the past. This would let Xms of events - // to accumulate in the write buffer before we converted it and forced the writer to allocate another. Other more - // sophisticated approaches would probably build a low overhead synchronization mechanism to read and write the - // buffer at the same time. - LARGE_INTEGER stopTimeStamp; - QueryPerformanceCounter(&stopTimeStamp); - MoveNextEventAnyThread(stopTimeStamp); - return GetCurrentEvent(); -} - -CLREvent *EventPipeBufferManager::GetWaitEvent() -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(m_waitEvent.IsValid()); - return &m_waitEvent; -} - -EventPipeEventInstance* EventPipeBufferManager::GetCurrentEvent() -{ - LIMITED_METHOD_CONTRACT; - return m_pCurrentEvent; -} - -unsigned int EventPipeBufferManager::GetCurrentSequenceNumber() -{ - LIMITED_METHOD_CONTRACT; - return m_pCurrentBuffer->GetCurrentSequenceNumber(); -} - -EventPipeBuffer* EventPipeBufferManager::GetCurrentEventBuffer() -{ - LIMITED_METHOD_CONTRACT; - return m_pCurrentBuffer; -} - -EventPipeBufferList* EventPipeBufferManager::GetCurrentEventBufferList() -{ - LIMITED_METHOD_CONTRACT; - return m_pCurrentBufferList; -} - -void EventPipeBufferManager::MoveNextEventAnyThread(LARGE_INTEGER stopTimeStamp) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(!m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - if (m_pCurrentEvent != nullptr) - { - m_pCurrentBuffer->MoveNextReadEvent(); - } - m_pCurrentEvent = nullptr; - m_pCurrentBuffer = nullptr; - m_pCurrentBufferList = nullptr; - - // We need to do this in two steps because we can't hold m_lock and EventPipeThread::m_lock - // at the same time. - - // Step 1 - while holding m_lock get the oldest buffer from each thread - CQuickArrayList bufferList; - CQuickArrayList bufferListList; - { - SpinLockHolder _slh(&m_lock); - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - EventPipeBufferList *pBufferList = pElem->GetValue()->GetBufferList(); - EventPipeBuffer *pBuffer = pBufferList->GetHead(); - if (pBuffer != nullptr && - pBuffer->GetCreationTimeStamp().QuadPart < stopTimeStamp.QuadPart) - { - bufferListList.Push(pBufferList); - bufferList.Push(pBuffer); - } - pElem = m_pThreadSessionStateList->GetNext(pElem); - } - } - - // Step 2 - iterate the cached list to find the one with the oldest event. This may require - // converting some of the buffers from writable to readable, and that in turn requires - // taking the associated EventPipeThread::m_lock for thread that was writing to that buffer. - LARGE_INTEGER curOldestTime = stopTimeStamp; - for (size_t i = 0; i < bufferList.Size(); i++) - { - EventPipeBufferList *pBufferList = bufferListList[i]; - EventPipeBuffer *pHeadBuffer = bufferList[i]; - EventPipeBuffer *pBuffer = AdvanceToNonEmptyBuffer(pBufferList, pHeadBuffer, stopTimeStamp); - if (pBuffer == nullptr) - { - // there weren't any non-empty buffers in that list prior to stopTimeStamp - continue; - } - // Peek the next event out of the buffer. - EventPipeEventInstance *pNext = pBuffer->GetCurrentReadEvent(); - if (pNext != NULL) - { - // If it's the oldest event we've seen, then save it. - if (pNext->GetTimeStamp()->QuadPart < curOldestTime.QuadPart) - { - m_pCurrentEvent = pNext; - m_pCurrentBuffer = pBuffer; - m_pCurrentBufferList = pBufferList; - curOldestTime = *(m_pCurrentEvent->GetTimeStamp()); - } - } - } -} - -void EventPipeBufferManager::MoveNextEventSameThread(LARGE_INTEGER beforeTimeStamp) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(m_pCurrentEvent != nullptr); - PRECONDITION(m_pCurrentBuffer != nullptr); - PRECONDITION(m_pCurrentBufferList != nullptr); - PRECONDITION(!m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - //advance past the current event - m_pCurrentEvent = nullptr; - m_pCurrentBuffer->MoveNextReadEvent(); - - // Find the first buffer in the list, if any, which has an event in it - m_pCurrentBuffer = AdvanceToNonEmptyBuffer(m_pCurrentBufferList, m_pCurrentBuffer, beforeTimeStamp); - if (m_pCurrentBuffer == nullptr) - { - // no more buffers prior to stopTimeStamp - _ASSERTE(m_pCurrentEvent == nullptr); - _ASSERTE(m_pCurrentBuffer == nullptr); - m_pCurrentBufferList = nullptr; - return; - } - - // get the event from that buffer - EventPipeEventInstance* pNextEvent = m_pCurrentBuffer->GetCurrentReadEvent(); - LARGE_INTEGER nextTimeStamp = *pNextEvent->GetTimeStamp(); - if (nextTimeStamp.QuadPart >= beforeTimeStamp.QuadPart) - { - // event exists, but isn't early enough - m_pCurrentEvent = nullptr; - m_pCurrentBuffer = nullptr; - m_pCurrentBufferList = nullptr; - } - else - { - // event is early enough, set the new cursor - m_pCurrentEvent = pNextEvent; - _ASSERTE(m_pCurrentBuffer != nullptr); - _ASSERTE(m_pCurrentBufferList != nullptr); - } -} - -EventPipeBuffer* EventPipeBufferManager::AdvanceToNonEmptyBuffer(EventPipeBufferList* pBufferList, - EventPipeBuffer* pBuffer, - LARGE_INTEGER beforeTimeStamp) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(!m_lock.OwnedByCurrentThread()); - PRECONDITION(pBufferList != nullptr); - PRECONDITION(pBuffer != nullptr); - PRECONDITION(pBufferList->GetHead() == pBuffer); - } - CONTRACTL_END; - - EventPipeBuffer* pCurrentBuffer = pBuffer; - while (true) - { - if (!TryConvertBufferToReadOnly(pCurrentBuffer)) - { - // the writer thread hasn't yet stored this buffer into the m_pWriteBuffer - // field (there is a small time window after allocation in this state). - // This should be the only buffer remaining in the list and it has no - // events written into it so we are done iterating. - return nullptr; - } - if (pCurrentBuffer->GetCurrentReadEvent() != nullptr) - { - // found a non-empty buffer - return pCurrentBuffer; - } - { - SpinLockHolder _slh(&m_lock); - - // delete the empty buffer - EventPipeBuffer *pRemoved = pBufferList->GetAndRemoveHead(); - _ASSERTE(pCurrentBuffer == pRemoved); - DeAllocateBuffer(pRemoved); - - // get the next buffer - pCurrentBuffer = pBufferList->GetHead(); - if (pCurrentBuffer == nullptr || - pCurrentBuffer->GetCreationTimeStamp().QuadPart >= beforeTimeStamp.QuadPart) - { - // no more buffers in the list before this timestamp, we're done - return nullptr; - } - } - } -} - -bool EventPipeBufferManager::TryConvertBufferToReadOnly(EventPipeBuffer* pNewReadBuffer) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pNewReadBuffer != nullptr); - PRECONDITION(!m_lock.OwnedByCurrentThread()); - } - CONTRACTL_END; - - // if already readable, nothing to do - if (pNewReadBuffer->GetVolatileState() == EventPipeBufferState::READ_ONLY) - { - return true; - } - - // if not yet readable, disable the thread from writing to it which causes - // it to become readable - { - EventPipeThread* pThread = pNewReadBuffer->GetWriterThread(); - SpinLockHolder _slh(pThread->GetLock()); - EventPipeThreadSessionState *const pSessionState = pThread->GetSessionState(m_pSession); - if (pSessionState->GetWriteBuffer() == pNewReadBuffer) - { - pSessionState->SetWriteBuffer(nullptr); - _ASSERTE(pNewReadBuffer->GetVolatileState() == EventPipeBufferState::READ_ONLY); - return true; - } - } - - // It is possible that EventPipeBufferList::TryGetBuffer(...) returns a writable buffer - // yet it is not returned as EventPipeThread::GetWriteBuffer(...). This is because - // EventPipeBufferManager::AllocateBufferForThread() insert the new writable buffer into - // the EventPipeBufferList first, and then it is added to the writable buffer hash table - // by EventPipeThread::SetWriteBuffer() next. The two operations are not atomic so it is possible - // to observe this partial state. - return pNewReadBuffer->GetVolatileState() == EventPipeBufferState::READ_ONLY; -} - -void EventPipeBufferManager::SuspendWriteEvent(uint32_t sessionIndex) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - // All calls to this method must be synchronized by our caller - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - CQuickArrayList threadList; - { - SpinLockHolder _slh(&m_lock); - _ASSERTE(EnsureConsistency()); - - // Find all threads that have used this buffer manager. - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - EventPipeThread *pThread = pElem->GetValue()->GetThread(); - threadList.Push(pThread); - pElem = m_pThreadSessionStateList->GetNext(pElem); - } - } - - // Iterate through all the threads, forcing them to relinquish any buffers stored in - // EventPipeThread::m_pWriteBuffer and prevent storing new ones. - for (size_t i = 0; i < threadList.Size(); i++) - { - EventPipeThread *pThread = threadList[i]; - { - SpinLockHolder _slh(pThread->GetLock()); - EventPipeThreadSessionState *const pSessionState = pThread->GetSessionState(m_pSession); - pSessionState->SetWriteBuffer(nullptr); - } - } -} - -void EventPipeBufferManager::DeAllocateBuffers() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - CQuickArrayList threadSessionStatesToRemove; - - { - // Take the buffer manager manipulation lock - SpinLockHolder _slh(&m_lock); - - _ASSERTE(EnsureConsistency()); - - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - // Get the list and determine if we can free it. - EventPipeThreadSessionState *pSessionState = pElem->GetValue(); - EventPipeBufferList *pBufferList = pSessionState->GetBufferList(); - EventPipeThread *pThread = pSessionState->GetThread(); - pSessionState->SetBufferList(nullptr); - - // Iterate over all nodes in the buffer list and deallocate them. - EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead(); - while (pBuffer != NULL) - { - DeAllocateBuffer(pBuffer); - pBuffer = pBufferList->GetAndRemoveHead(); - } - - // Now that all the buffer list elements have been freed, free the list itself. - delete(pBufferList); - pBufferList = NULL; - - // Remove the session state from the session state list. - pElem = m_pThreadSessionStateList->FindAndRemove(pElem); - _ASSERTE(pElem != NULL); - - SListElem *pCurElem = pElem; - pElem = m_pThreadSessionStateList->GetNext(pElem); - delete (pCurElem); - - // And finally queue the removal of the SessionState from the thread - EX_TRY - { - threadSessionStatesToRemove.Push(pSessionState); - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions); - } - } - - // remove and delete the session state - for (size_t i = 0; i < threadSessionStatesToRemove.Size(); i++) - { - EventPipeThreadSessionState* pThreadSessionState = threadSessionStatesToRemove[i]; - // The strong reference from session state -> thread might be the very last reference - // We need to ensure the thread doesn't die until we can release the lock - EventPipeThreadHolder pThread = pThreadSessionState->GetThread(); - { - SpinLockHolder _slh(pThreadSessionState->GetThread()->GetLock()); - pThreadSessionState->GetThread()->DeleteSessionState(pThreadSessionState->GetSession()); - } - } -} - -#ifdef _DEBUG -bool EventPipeBufferManager::EnsureConsistency() -{ - LIMITED_METHOD_CONTRACT; - - SListElem *pElem = m_pThreadSessionStateList->GetHead(); - while (pElem != NULL) - { - EventPipeBufferList *pBufferList = pElem->GetValue()->GetBufferList(); - - _ASSERTE(pBufferList->EnsureConsistency()); - - pElem = m_pThreadSessionStateList->GetNext(pElem); - } - - return true; -} -#endif // _DEBUG - -EventPipeBufferList::EventPipeBufferList(EventPipeBufferManager *pManager, EventPipeThread *pThread) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(pManager != nullptr); - _ASSERTE(pThread != nullptr); // TODO: Is this the correct assumption? - - m_pManager = pManager; - m_pThread = pThread; - m_pHeadBuffer = NULL; - m_pTailBuffer = NULL; - m_bufferCount = 0; - m_lastReadSequenceNumber = 0; -} - -EventPipeBuffer *EventPipeBufferList::GetHead() -{ - LIMITED_METHOD_CONTRACT; - return m_pHeadBuffer; -} - -EventPipeBuffer *EventPipeBufferList::GetTail() -{ - LIMITED_METHOD_CONTRACT; - return m_pTailBuffer; -} - -void EventPipeBufferList::InsertTail(EventPipeBuffer *pBuffer) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pBuffer != NULL); - PRECONDITION(EnsureConsistency()); - // Ensure that the input buffer didn't come from another list that was improperly cleaned up. - PRECONDITION((pBuffer->GetNext() == NULL) && (pBuffer->GetPrevious() == NULL)); - } - CONTRACTL_END; - - // First node in the list. - if (m_pTailBuffer == NULL) - { - m_pHeadBuffer = m_pTailBuffer = pBuffer; - } - else - { - // Set links between the old and new tail nodes. - m_pTailBuffer->SetNext(pBuffer); - pBuffer->SetPrevious(m_pTailBuffer); - - // Set the new tail node. - m_pTailBuffer = pBuffer; - } - - m_bufferCount++; - - _ASSERTE(EnsureConsistency()); -} - -EventPipeBuffer *EventPipeBufferList::GetAndRemoveHead() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(EnsureConsistency()); - - EventPipeBuffer *pRetBuffer = NULL; - if (m_pHeadBuffer != NULL) - { - // Save the head node. - pRetBuffer = m_pHeadBuffer; - - // Set the new head node. - m_pHeadBuffer = m_pHeadBuffer->GetNext(); - - // Update the head node's previous pointer. - if (m_pHeadBuffer != NULL) - { - m_pHeadBuffer->SetPrevious(NULL); - } - else - { - // We just removed the last buffer from the list. - // Make sure both head and tail pointers are NULL. - m_pTailBuffer = NULL; - } - - // Clear the next pointer of the old head node. - pRetBuffer->SetNext(NULL); - - // Ensure that the old head node has no dangling references. - _ASSERTE((pRetBuffer->GetNext() == NULL) && (pRetBuffer->GetPrevious() == NULL)); - - // Decrement the count of buffers in the list. - m_bufferCount--; - } - - _ASSERTE(EnsureConsistency()); - - return pRetBuffer; -} - -unsigned int EventPipeBufferList::GetCount() const -{ - LIMITED_METHOD_CONTRACT; - return m_bufferCount; -} - -EventPipeThread *EventPipeBufferList::GetThread() -{ - LIMITED_METHOD_CONTRACT; - return m_pThread; -} - -unsigned int EventPipeBufferList::GetLastReadSequenceNumber() -{ - LIMITED_METHOD_CONTRACT; - return m_lastReadSequenceNumber; -} - -void EventPipeBufferList::SetLastReadSequenceNumber(unsigned int sequenceNumber) -{ - LIMITED_METHOD_CONTRACT; - m_lastReadSequenceNumber = sequenceNumber; -} - -#ifdef _DEBUG -bool EventPipeBufferList::EnsureConsistency() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Either the head and tail nodes are both NULL or both are non-NULL. - _ASSERTE((m_pHeadBuffer == NULL && m_pTailBuffer == NULL) || (m_pHeadBuffer != NULL && m_pTailBuffer != NULL)); - - // If the list is NULL, check the count and return. - if (m_pHeadBuffer == NULL) - { - _ASSERTE(m_bufferCount == 0); - return true; - } - - // If the list is non-NULL, walk the list forward until we get to the end. - unsigned int nodeCount = (m_pHeadBuffer != NULL) ? 1 : 0; - EventPipeBuffer *pIter = m_pHeadBuffer; - while (pIter->GetNext() != NULL) - { - pIter = pIter->GetNext(); - nodeCount++; - - // Check for consistency of the buffer itself. - // NOTE: We can't check the last buffer because the owning thread could - // be writing to it, which could result in false asserts. - if (pIter->GetNext() != NULL) - { - _ASSERTE(pIter->EnsureConsistency()); - } - - // Check for cycles. - _ASSERTE(nodeCount <= m_bufferCount); - } - - // When we're done with the walk, pIter must point to the tail node. - _ASSERTE(pIter == m_pTailBuffer); - - // Node count must equal the buffer count. - _ASSERTE(nodeCount == m_bufferCount); - - // Now, walk the list in reverse. - pIter = m_pTailBuffer; - nodeCount = (m_pTailBuffer != NULL) ? 1 : 0; - while (pIter->GetPrevious() != NULL) - { - pIter = pIter->GetPrevious(); - nodeCount++; - - // Check for cycles. - _ASSERTE(nodeCount <= m_bufferCount); - } - - // When we're done with the reverse walk, pIter must point to the head node. - _ASSERTE(pIter == m_pHeadBuffer); - - // Node count must equal the buffer count. - _ASSERTE(nodeCount == m_bufferCount); - - // We're done. - return true; -} -#endif // _DEBUG - -#ifdef DEBUG -bool EventPipeBufferList::IsBufferManagerLockOwnedByCurrentThread() -{ - return m_pManager->IsLockOwnedByCurrentThread(); -} -#endif - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipebuffermanager.h b/src/coreclr/vm/eventpipebuffermanager.h deleted file mode 100644 index 73b80bfaa64bc..0000000000000 --- a/src/coreclr/vm/eventpipebuffermanager.h +++ /dev/null @@ -1,261 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_BUFFERMANAGER_H__ -#define __EVENTPIPE_BUFFERMANAGER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipeeventinstance.h" -#include "eventpipethread.h" -#include "spinlock.h" - -class EventPipeBuffer; -class EventPipeBufferList; -class EventPipeBufferManager; -class EventPipeFile; -class EventPipeSession; -class EventPipeThread; -struct EventPipeSequencePoint; - -class EventPipeBufferManager -{ - - // Declare friends. - friend class EventPipeBufferList; - -private: - // The session this buffer manager belongs to - EventPipeSession* m_pSession; - - // A list of per-thread session state - // Each entry in this list represents the session state owned by a single thread - // which includes the list of buffers the thread has written and its current - // event sequence number. The EventPipeThread object also has a pointer to the - // session state contained in this list. This ensures that each thread can access - // its own data, while at the same time, ensuring that when a thread is destroyed, - // we keep the buffers around without having to perform any migration or - // book-keeping. - SList> *m_pThreadSessionStateList; - - // The total allocation size of buffers under management. - size_t m_sizeOfAllBuffers; - - // The maximum allowable size of buffers under management. - // Attempted allocations above this threshold result in - // dropped events. - size_t m_maxSizeOfAllBuffers; - - // The amount of allocations we can do at this moment before - // triggering a sequence point - size_t m_remainingSequencePointAllocationBudget; - - // The total amount of allocations we can do after one sequence - // point before triggering the next one - size_t m_sequencePointAllocationBudget; - - // A queue of sequence points. - SList m_sequencePoints; - - // Lock to protect access to the per-thread buffer list and total allocation size. - SpinLock m_lock; - - // Event for synchronizing real time reading - CLREvent m_waitEvent; - - // Iterator state for reader thread - // These are not protected by m_lock and expected to only be used on the reader thread - EventPipeEventInstance* m_pCurrentEvent; - EventPipeBuffer* m_pCurrentBuffer; - EventPipeBufferList* m_pCurrentBufferList; - -#ifdef _DEBUG - // For debugging purposes. - unsigned int m_numBuffersAllocated; - unsigned int m_numBuffersStolen; - unsigned int m_numBuffersLeaked; - Volatile m_numEventsStored; - Volatile m_numEventsDropped; - unsigned long m_numEventsWritten; -#endif // _DEBUG - - // Allocate a new buffer for the specified thread. - // This function will store the buffer in the thread's buffer list for future use and also return it here. - // A NULL return value means that a buffer could not be allocated. - EventPipeBuffer* AllocateBufferForThread(EventPipeThreadSessionState* pSessionState, unsigned int requestSize, BOOL & writeSuspended); - - // Add a buffer to the thread buffer list. - void AddBufferToThreadBufferList(EventPipeBufferList *pThreadBuffers, EventPipeBuffer *pBuffer); - - // Enqueue a sequence point into the queue. - void EnqueueSequencePoint(EventPipeSequencePoint* pEnqueuedSequencePoint); - - // Dequeue a sequence point from the queue. This is a no-op if the queue is empty. - void DequeueSequencePoint(); - - // Peek the first sequence point in the queue. Returns FALSE if the queue is empty. - bool TryPeekSequencePoint(EventPipeSequencePoint** ppSequencePoint); - - // Inits a sequence point that has the list of current threads and sequence - // numbers (Requires m_lock is already held) - void InitSequencePointThreadListHaveLock(EventPipeSequencePoint* pSequencePoint); - - // De-allocates the input buffer. - void DeAllocateBuffer(EventPipeBuffer *pBuffer); - - // Detaches this buffer from an active writer thread and marks it read-only so that the reader - // thread can use it. If the writer thread has not yet stored the buffer into its thread-local - // slot it will not be converted, but such buffers have no events in them so there is no reason - // to read them. - bool TryConvertBufferToReadOnly(EventPipeBuffer* pNewReadBuffer); - - // Finds the first buffer in EventPipeBufferList that has a readable event prior to beforeTimeStamp, - // starting with pBuffer - EventPipeBuffer* AdvanceToNonEmptyBuffer(EventPipeBufferList* pBufferList, - EventPipeBuffer* pBuffer, - LARGE_INTEGER beforeTimeStamp); - - // -------------- Reader Iteration API ---------------- - // An iterator that can enumerate all the events which have been written into this buffer manager. - // Initially the iterator starts uninitialized and GetCurrentEvent() returns NULL. Calling MoveNextXXX() - // attempts to advance the cursor to the next event. If there is no event prior to stopTimeStamp then - // the GetCurrentEvent() again returns NULL, otherwise it returns that event. The event pointer returned - // by GetCurrentEvent() is valid until MoveNextXXX() is called again. Once all events in a buffer have - // been read the iterator will delete that buffer from the pool. - - // Moves to the next oldest event searching across all threads. If there is no event older than - // stopTimeStamp then GetCurrentEvent() will return NULL. - void MoveNextEventAnyThread(LARGE_INTEGER stopTimeStamp); - - // Moves to the next oldest event from the same thread as the current event. If there is no event - // older than stopTimeStamp then GetCurrentEvent() will return NULL. This should only be called - // when GetCurrentEvent() is non-null (because we need to know what thread's events to iterate) - void MoveNextEventSameThread(LARGE_INTEGER stopTimeStamp); - - // Returns the current event the iteration cursor is on, or NULL if the iteration is unitialized/ - // the last call to MoveNextXXX() didn't find any suitable event. - EventPipeEventInstance* GetCurrentEvent(); - - // Gets the sequence number corresponding to event from GetCurrentEvent() - unsigned int GetCurrentSequenceNumber(); - - // Gets the buffer corresponding to event from GetCurrentEvent() - EventPipeBuffer* GetCurrentEventBuffer(); - - // Gets the buffer list corresponding to event from GetCurrentEvent() - EventPipeBufferList* GetCurrentEventBufferList(); - -public: - - EventPipeBufferManager(EventPipeSession* pEventSession, size_t maxSizeOfAllBuffers, size_t sequencePointAllocationBudget); - ~EventPipeBufferManager(); - - // Write an event to the input thread's current event buffer. - // An optional eventThread can be provided for sample profiler events. - // This is because the thread that writes the events is not the same as the "event thread". - // An optional stack trace can be provided for sample profiler events. - // Otherwise, if a stack trace is needed, one will be automatically collected. - bool WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL); - - // Inits a sequence point that has the list of current threads and sequence - // numbers - void InitSequencePointThreadList(EventPipeSequencePoint* pSequencePoint); - - // READ_ONLY state and no new EventPipeBuffers or EventPipeBufferLists can be created. Calls to - // WriteEvent that start during the suspension period or were in progress but hadn't yet recorded - // their event into a buffer before the start of the suspension period will return false and the - // event will not be recorded. Any events that not recorded as a result of this suspension will be - // treated the same as events that were not recorded due to configuration. - // EXPECTED USAGE: First the caller will disable all events via configuration, then call - // SuspendWriteEvent() to force any WriteEvent calls that may still be in progress to either - // finish or cancel. After that all BufferLists and Buffers can be safely drained and/or deleted. - void SuspendWriteEvent(uint32_t sessionIndex); - - // Write the contents of the managed buffers to the specified file. - // The stopTimeStamp is used to determine when tracing was stopped to ensure that we - // skip any events that might be partially written due to races when tracing is stopped. - void WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp, bool *pEventsWritten); - void WriteAllBuffersToFileV3(EventPipeFile *pFastSerializableObject, LARGE_INTEGER stopTimeStamp, bool *pEventsWritten); - void WriteAllBuffersToFileV4(EventPipeFile *pFastSerializableObject, LARGE_INTEGER stopTimeStamp, bool *pEventsWritten); - - // Attempt to de-allocate resources as best we can. It is possible for some buffers to leak because - // threads can be in the middle of a write operation and get blocked, and we may not get an opportunity - // to free their buffer for a very long time. - void DeAllocateBuffers(); - - // Get next event. This is used to dispatch events to EventListener. - EventPipeEventInstance* GetNextEvent(); - - CLREvent *GetWaitEvent(); - -#ifdef _DEBUG - bool EnsureConsistency(); - bool IsLockOwnedByCurrentThread(); -#endif // _DEBUG -}; - -// Represents a list of buffers associated with a specific thread. -class EventPipeBufferList -{ -private: - - // The buffer manager that owns this list. - EventPipeBufferManager *m_pManager; - - // The thread which writes to the buffers in this list - EventPipeThreadHolder m_pThread; - - // Buffers are stored in an intrusive linked-list from oldest to newest. - // Head is the oldest buffer. Tail is the newest (and currently used) buffer. - EventPipeBuffer *m_pHeadBuffer; - EventPipeBuffer *m_pTailBuffer; - - // The number of buffers in the list. - unsigned int m_bufferCount; - - // The sequence number of the last event that was read, only - // updated/read by the reader thread. - unsigned int m_lastReadSequenceNumber; - -public: - - EventPipeBufferList(EventPipeBufferManager *pManager, EventPipeThread* pThread); - - // Get the head node of the list. - EventPipeBuffer* GetHead(); - - // Get the tail node of the list. - EventPipeBuffer* GetTail(); - - // Insert a new buffer at the tail of the list. - void InsertTail(EventPipeBuffer *pBuffer); - - // Remove the head node of the list. - EventPipeBuffer* GetAndRemoveHead(); - - // Get the count of buffers in the list. - unsigned int GetCount() const; - - // Get the thread associated with this list. - EventPipeThread* GetThread(); - - // Read/Write the last read sequence number - unsigned int GetLastReadSequenceNumber(); - void SetLastReadSequenceNumber(unsigned int sequenceNumber); - -#ifdef _DEBUG - // Validate the consistency of the list. - // This function will assert if the list is in an inconsistent state. - bool EnsureConsistency(); -#endif // _DEBUG - -#ifdef DEBUG - bool IsBufferManagerLockOwnedByCurrentThread(); -#endif // DEBUG -}; - - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_BUFFERMANAGER_H__ diff --git a/src/coreclr/vm/eventpipecommontypes.cpp b/src/coreclr/vm/eventpipecommontypes.cpp deleted file mode 100644 index f43b4873a6e45..0000000000000 --- a/src/coreclr/vm/eventpipecommontypes.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipecommontypes.h" - -#ifdef FEATURE_PERFTRACING - -void EventPipeProviderCallbackDataQueue::Enqueue(EventPipeProviderCallbackData *pEventPipeProviderCallbackData) -{ - SListElem *listnode = new SListElem(std::move(*pEventPipeProviderCallbackData)); // throws - list.InsertTail(listnode); -} - -bool EventPipeProviderCallbackDataQueue::TryDequeue(EventPipeProviderCallbackData *pEventPipeProviderCallbackData) -{ - if (list.IsEmpty()) - return false; - - SListElem *listnode = list.RemoveHead(); - *pEventPipeProviderCallbackData = std::move(listnode->m_Value); - delete listnode; - return true; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipecommontypes.h b/src/coreclr/vm/eventpipecommontypes.h deleted file mode 100644 index 8585eae158bb1..0000000000000 --- a/src/coreclr/vm/eventpipecommontypes.h +++ /dev/null @@ -1,279 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_PROVIDERCALLBACKDATA_H__ -#define __EVENTPIPE_PROVIDERCALLBACKDATA_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" - -class EventPipeProvider; - -enum class EventPipeEventLevel -{ - LogAlways, - Critical, - Error, - Warning, - Informational, - Verbose -}; - -struct EventPipeProviderConfiguration -{ - friend class EventPipeProviderConfigurationAdapter; - -private: - LPCWSTR m_pProviderName = nullptr; - UINT64 m_keywords = 0; - UINT32 m_loggingLevel = 0; - LPCWSTR m_pFilterData = nullptr; - -public: - EventPipeProviderConfiguration() = default; - - EventPipeProviderConfiguration(LPCWSTR pProviderName, UINT64 keywords, UINT32 loggingLevel, LPCWSTR pFilterData) : - m_pProviderName(pProviderName), - m_keywords(keywords), - m_loggingLevel(loggingLevel), - m_pFilterData(pFilterData) - { - } - - LPCWSTR GetProviderName() const - { - LIMITED_METHOD_CONTRACT; - return m_pProviderName; - } - - UINT64 GetKeywords() const - { - LIMITED_METHOD_CONTRACT; - return m_keywords; - } - - UINT32 GetLevel() const - { - LIMITED_METHOD_CONTRACT; - return m_loggingLevel; - } - - LPCWSTR GetFilterData() const - { - LIMITED_METHOD_CONTRACT; - return m_pFilterData; - } -}; - -// 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. -// TODO: EventFilterDescriptor and EventData (defined below) are the same. -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, - ULONG IsEnabled, - UCHAR Level, - ULONGLONG MatchAnyKeywords, - ULONGLONG MatchAllKeywords, - EventFilterDescriptor *FilterData, - void *CallbackContext); - -class EventPipeProviderCallbackData -{ -public: - EventPipeProviderCallbackData(): - m_pFilterData(nullptr), - m_pCallbackFunction(nullptr), - m_enabled(false), - m_keywords(0), - m_providerLevel(EventPipeEventLevel::LogAlways), - m_pCallbackData(nullptr), - m_pProvider(nullptr) - { - - } - - EventPipeProviderCallbackData(LPCWSTR pFilterData, - EventPipeCallback pCallbackFunction, - bool enabled, - INT64 keywords, - EventPipeEventLevel providerLevel, - void* pCallbackData, - EventPipeProvider *pProvider) : - m_pFilterData(nullptr), - m_pCallbackFunction(pCallbackFunction), - m_enabled(enabled), - m_keywords(keywords), - m_providerLevel(providerLevel), - m_pCallbackData(pCallbackData), - m_pProvider(pProvider) - { - if (pFilterData != nullptr) - { - // This is the only way to create an EventPipeProviderCallbackData that will copy the - // filter data. The copying is intentional, because sessions die before callbacks happen - // so we cannot cache a pointer to the session's filter data. - size_t bufSize = wcslen(pFilterData) + 1; - m_pFilterData = new WCHAR[bufSize]; - wcscpy_s(m_pFilterData, bufSize, pFilterData); - } - } - - EventPipeProviderCallbackData(EventPipeProviderCallbackData &&other) - : EventPipeProviderCallbackData() - { - *this = std::move(other); - } - - EventPipeProviderCallbackData &operator=(EventPipeProviderCallbackData &&other) - { - std::swap(m_pFilterData, other.m_pFilterData); - m_pCallbackFunction = other.m_pCallbackFunction; - m_enabled = other.m_enabled; - m_keywords = other.m_keywords; - m_providerLevel = other.m_providerLevel; - m_pCallbackData = other.m_pCallbackData; - m_pProvider = other.m_pProvider; - - return *this; - } - - // We don't want to be unintentionally copying and deleting the filter data any more - // than we have to. Moving (above) is fine, but copying should be avoided. - EventPipeProviderCallbackData(const EventPipeProviderCallbackData &other) = delete; - EventPipeProviderCallbackData &operator=(const EventPipeProviderCallbackData &other) = delete; - - ~EventPipeProviderCallbackData() - { - if (m_pFilterData != nullptr) - { - delete[] m_pFilterData; - m_pFilterData = nullptr; - } - } - - LPCWSTR GetFilterData() const - { - return m_pFilterData; - } - - EventPipeCallback GetCallbackFunction() const - { - return m_pCallbackFunction; - } - - bool GetEnabled() const - { - return m_enabled; - } - - INT64 GetKeywords() const - { - return m_keywords; - } - - EventPipeEventLevel GetProviderLevel() const - { - return m_providerLevel; - } - - void *GetCallbackData() const - { - return m_pCallbackData; - } - - EventPipeProvider *GetProvider() const - { - return m_pProvider; - } - -private: - WCHAR *m_pFilterData; - EventPipeCallback m_pCallbackFunction; - bool m_enabled; - INT64 m_keywords; - EventPipeEventLevel m_providerLevel; - void* m_pCallbackData; - EventPipeProvider *m_pProvider; -}; - -class EventPipeProviderCallbackDataQueue -{ -public: - void Enqueue(EventPipeProviderCallbackData *pEventPipeProviderCallbackData); - bool TryDequeue(EventPipeProviderCallbackData* pEventPipeProviderCallbackData); - -private: - SList> list; -}; - -template -class EventPipeIterator -{ -private: - SList> *m_pList; - typename SList>::Iterator m_iterator; - -public: - EventPipeIterator(SList> *pList) : - m_pList(pList), - m_iterator(pList->begin()) - { - _ASSERTE(m_pList != nullptr); - } - - bool Next(T *ppProvider) - { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(ppProvider != nullptr); - } - CONTRACTL_END; - - *ppProvider = *m_iterator; - ++m_iterator; - return m_iterator != m_pList->end(); - } - -}; - -typedef void (*EventPipeSessionSynchronousCallback)( - EventPipeProvider *provider, - DWORD eventId, - DWORD eventVersion, - ULONG cbMetadataBlob, - LPCBYTE metadataBlob, - ULONG cbEventData, - LPCBYTE eventData, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId, - Thread *pEventThread, - ULONG numStackFrames, - UINT_PTR stackFrames[]); - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_PROVIDERCALLBACKDATA_H__ diff --git a/src/coreclr/vm/eventpipeconfiguration.cpp b/src/coreclr/vm/eventpipeconfiguration.cpp deleted file mode 100644 index 346d015f9761f..0000000000000 --- a/src/coreclr/vm/eventpipeconfiguration.cpp +++ /dev/null @@ -1,526 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipeconfiguration.h" -#include "eventpipeeventinstance.h" -#include "eventpipesessionprovider.h" -#include "eventpipeprovider.h" -#include "eventpipesession.h" -#include "win32threadpool.h" - -#ifdef FEATURE_PERFTRACING - -const WCHAR *EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration"); - -void EventPipeConfiguration::Initialize() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(m_pProviderList == nullptr); - PRECONDITION(m_pConfigProvider == nullptr); - PRECONDITION(m_pMetadataEvent == nullptr); - } - CONTRACTL_END; - - m_pProviderList = new SList>(); - - EventPipe::RunWithCallbackPostponed([&](EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) { - // Create the configuration provider. - m_pConfigProvider = CreateProvider(SL(s_configurationProviderName), NULL, NULL, pEventPipeProviderCallbackDataQueue); - }); - - // Create the metadata event. - m_pMetadataEvent = m_pConfigProvider->AddEvent( - 0, /* eventID */ - 0, /* keywords */ - 0, /* eventVersion */ - EventPipeEventLevel::LogAlways, - false); /* needStack */ -} - -void EventPipeConfiguration::Shutdown() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pConfigProvider != nullptr) - { - // This unregisters the provider, which takes a - // HOST_BREAKABLE lock - EX_TRY - { - DeleteProvider(m_pConfigProvider); - } - EX_CATCH {} - EX_END_CATCH(SwallowAllExceptions); - - m_pConfigProvider = nullptr; - } - - if (m_pProviderList != nullptr) - { - // We swallow exceptions here because the HOST_BREAKABLE - // lock may throw and this destructor gets called in throw - // intolerant places. If that happens the provider list will leak - EX_TRY - { - // Take the lock before manipulating the list. - CrstHolder _crst(EventPipe::GetLock()); - - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != nullptr) - { - // We don't delete provider itself because it can be in-use - SListElem *pCurElem = pElem; - pElem = m_pProviderList->GetNext(pElem); - delete pCurElem; - } - - delete m_pProviderList; - } - EX_CATCH {} - EX_END_CATCH(SwallowAllExceptions); - - m_pProviderList = nullptr; - } -} - -EventPipeProvider *EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // Allocate a new provider. - EventPipeProvider *pProvider = new EventPipeProvider(this, providerName, pCallbackFunction, pCallbackData); - - // Register the provider with the configuration system. - RegisterProvider(*pProvider, pEventPipeProviderCallbackDataQueue); - - return pProvider; -} - -void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pProvider != NULL); - } - CONTRACTL_END; - - if (pProvider == NULL) - return; - - // Unregister the provider. - UnregisterProvider(*pProvider); - - // Free the provider itself. - delete pProvider; -} - -bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // See if we've already registered this provider. When the portable thread pool is being used, allow there to be multiple - // DotNETRuntime providers, as the portable thread pool temporarily uses an event source on the managed side with the same - // provider name. - // TODO: This change to allow multiple DotNETRuntime providers is temporary to get EventPipe working for - // PortableThreadPoolEventSource. Once a long-term solution is figured out, this change should be reverted. See - // https://github.com/dotnet/runtime/issues/38763 for more information. - if (!ThreadpoolMgr::UsePortableThreadPool() || !provider.GetProviderName().Equals(W("Microsoft-Windows-DotNETRuntime"))) - { - EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName()); - if (pExistingProvider != nullptr) - return false; - } - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != nullptr) - { - // The provider has not been registered, so register it. - m_pProviderList->InsertTail(new SListElem(&provider)); - } - - INT64 keywordForAllSessions; - EventPipeEventLevel levelForAllSessions; - ComputeKeywordAndLevel(provider, /* out */ keywordForAllSessions, /* out */ levelForAllSessions); - - EventPipe::ForEachSession([&](EventPipeSession &session) { - // Set the provider configuration and enable it if it has been requested by a session. - EventPipeSessionProvider *pSessionProvider = GetSessionProvider(session, &provider); - if (pSessionProvider == nullptr) - return; - - EventPipeProviderCallbackData eventPipeProviderCallbackData = provider.SetConfiguration( - keywordForAllSessions, - levelForAllSessions, - session.GetMask(), - pSessionProvider->GetKeywords(), - pSessionProvider->GetLevel(), - pSessionProvider->GetFilterData()); - pEventPipeProviderCallbackDataQueue->Enqueue(&eventPipeProviderCallbackData); - }); - - return true; -} - -bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Take the lock before manipulating the provider list. - CrstHolder _crst(EventPipe::GetLock()); - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != NULL) - { - // Find the provider. - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != NULL) - { - if (pElem->GetValue() == &provider) - { - break; - } - - pElem = m_pProviderList->GetNext(pElem); - } - - // If we found the provider, remove it. - if (pElem != NULL) - { - if (m_pProviderList->FindAndRemove(pElem) != NULL) - { - delete pElem; - return true; - } - } - } - - return false; -} - -EventPipeProvider *EventPipeConfiguration::GetProvider(const SString &providerName) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Take the lock before touching the provider list to ensure no one tries to - // modify the list. - CrstHolder _crst(EventPipe::GetLock()); - - return GetProviderNoLock(providerName); -} - -EventPipeProvider *EventPipeConfiguration::GetProviderNoLock(const SString &providerName) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != NULL) - { - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != NULL) - { - EventPipeProvider *pProvider = pElem->GetValue(); - if (pProvider->GetProviderName().Equals(providerName)) - { - return pProvider; - } - - pElem = m_pProviderList->GetNext(pElem); - } - } - - return NULL; -} - -EventPipeSessionProvider *EventPipeConfiguration::GetSessionProvider(const EventPipeSession &session, const EventPipeProvider *pProvider) const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - return session.GetSessionProvider(pProvider); -} - -void EventPipeConfiguration::ComputeKeywordAndLevel(const EventPipeProvider& provider, INT64& keywordForAllSessions, EventPipeEventLevel& levelForAllSessions) const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - keywordForAllSessions = 0; - levelForAllSessions = EventPipeEventLevel::LogAlways; - EventPipe::ForEachSession([&](EventPipeSession &session) { - EventPipeSessionProvider *pSessionProvider = GetSessionProvider(session, &provider); - if (pSessionProvider != nullptr) - { - INT64 sessionKeyword = pSessionProvider->GetKeywords(); - EventPipeEventLevel sessionLevel = pSessionProvider->GetLevel(); - keywordForAllSessions = keywordForAllSessions | sessionKeyword; - levelForAllSessions = (sessionLevel > levelForAllSessions) ? sessionLevel : levelForAllSessions; - } - }); -} - -INT64 EventPipeConfiguration::ComputeEventEnabledMask(const EventPipeProvider& provider, INT64 keywords, EventPipeEventLevel eventLevel) const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - INT64 result = 0; - EventPipe::ForEachSession([&](EventPipeSession &session) { - EventPipeSessionProvider *pSessionProvider = GetSessionProvider(session, &provider); - if (pSessionProvider != nullptr) - { - INT64 sessionKeyword = pSessionProvider->GetKeywords(); - EventPipeEventLevel sessionLevel = pSessionProvider->GetLevel(); - // The event is enabled if: - // - The provider is enabled. - // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0. - // - The event level is LogAlways or the provider's verbosity level is set to greater than the event's verbosity level in the manifest. - bool providerEnabled = provider.Enabled(); - bool keywordEnabled = (keywords == 0) || ((sessionKeyword & keywords) != 0); - bool levelEnabled = ((eventLevel == EventPipeEventLevel::LogAlways) || (sessionLevel >= eventLevel)); - if (providerEnabled && keywordEnabled && levelEnabled) - { - result = result | session.GetMask(); - } - } - }); - return result; -} - -void EventPipeConfiguration::Enable(EventPipeSession &session, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != NULL) - { - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != NULL) - { - EventPipeProvider *pProvider = pElem->GetValue(); - - // Enable the provider if it has been configured. - EventPipeSessionProvider *pSessionProvider = GetSessionProvider(session, pProvider); - if (pSessionProvider != NULL) - { - INT64 keywordForAllSessions; - EventPipeEventLevel levelForAllSessions; - ComputeKeywordAndLevel(*pProvider, /* out */ keywordForAllSessions, /* out */ levelForAllSessions); - - EventPipeProviderCallbackData eventPipeProviderCallbackData = - pProvider->SetConfiguration( - keywordForAllSessions, - levelForAllSessions, - session.GetMask(), - pSessionProvider->GetKeywords(), - pSessionProvider->GetLevel(), - pSessionProvider->GetFilterData()); - pEventPipeProviderCallbackDataQueue->Enqueue(&eventPipeProviderCallbackData); - } - - pElem = m_pProviderList->GetNext(pElem); - } - } -} - -void EventPipeConfiguration::Disable(const EventPipeSession &session, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != NULL) - { - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != NULL) - { - EventPipeProvider *pProvider = pElem->GetValue(); - if (pProvider->IsEnabled(session.GetMask())) - { - EventPipeSessionProvider *pSessionProvider = GetSessionProvider(session, pProvider); - if (pSessionProvider != nullptr) - { - INT64 keywordForAllSessions; - EventPipeEventLevel levelForAllSessions; - ComputeKeywordAndLevel(*pProvider, /* out */ keywordForAllSessions, /* out */ levelForAllSessions); - - EventPipeProviderCallbackData eventPipeProviderCallbackData = pProvider->UnsetConfiguration( - keywordForAllSessions, - levelForAllSessions, - session.GetMask(), - pSessionProvider->GetKeywords(), - pSessionProvider->GetLevel(), - pSessionProvider->GetFilterData()); - pEventPipeProviderCallbackDataQueue->Enqueue(&eventPipeProviderCallbackData); - } - } - - pElem = m_pProviderList->GetNext(pElem); - } - } -} - -EventPipeEventInstance *EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // The payload of the event should contain: - // - Metadata ID - // - GUID ProviderID. - // - Optional event description payload. - - // Calculate the size of the event. - EventPipeEvent &sourceEvent = *sourceInstance.GetEvent(); - const SString &providerName = sourceEvent.GetProvider()->GetProviderName(); - BYTE *pPayloadData = sourceEvent.GetMetadata(); - unsigned int payloadLength = sourceEvent.GetMetadataLength(); - unsigned int providerNameLength = (providerName.GetCount() + 1) * sizeof(WCHAR); - unsigned int instancePayloadSize = sizeof(metadataId) + providerNameLength + payloadLength; - - // Allocate the payload. - BYTE *pInstancePayload = new BYTE[instancePayloadSize]; - - // Fill the buffer with the payload. - BYTE *currentPtr = pInstancePayload; - - memcpy(currentPtr, &metadataId, sizeof(metadataId)); - currentPtr += sizeof(metadataId); - - memcpy(currentPtr, (BYTE *)providerName.GetUnicode(), providerNameLength); - currentPtr += providerNameLength; - - // Write the incoming payload data. - memcpy(currentPtr, pPayloadData, payloadLength); - - // Construct the event instance. - EventPipeEventInstance *pInstance = new EventPipeEventInstance( - *m_pMetadataEvent, - EventPipe::GetCurrentProcessorNumber(), -#ifdef TARGET_UNIX - PAL_GetCurrentOSThreadId(), -#else - GetCurrentThreadId(), -#endif - pInstancePayload, - instancePayloadSize, - NULL /* pActivityId */, - NULL /* pRelatedActivityId */); - _ASSERTE(!m_pMetadataEvent->NeedStack()); - - // Set the timestamp to match the source event, because the metadata event - // will be emitted right before the source event. - pInstance->SetTimeStamp(*sourceInstance.GetTimeStamp()); - - return pInstance; -} - -void EventPipeConfiguration::DeleteDeferredProviders() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - // Lock must be held by EventPipe::Disable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // The provider list should be non-NULL, but can be NULL on shutdown. - if (m_pProviderList != NULL) - { - SListElem *pElem = m_pProviderList->GetHead(); - while (pElem != NULL) - { - EventPipeProvider *pProvider = pElem->GetValue(); - pElem = m_pProviderList->GetNext(pElem); - if (pProvider->GetDeleteDeferred()) - { - DeleteProvider(pProvider); - } - } - } -} -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeconfiguration.h b/src/coreclr/vm/eventpipeconfiguration.h deleted file mode 100644 index cf3bd0b2aa718..0000000000000 --- a/src/coreclr/vm/eventpipeconfiguration.h +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_CONFIGURATION_H__ -#define __EVENTPIPE_CONFIGURATION_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "slist.h" - -class EventPipeSessionProvider; -class EventPipeEvent; -class EventPipeEventInstance; -class EventPipeProvider; -class EventPipeSession; - -class EventPipeConfiguration -{ -public: - // Perform initialization that cannot be performed in the constructor. - void Initialize(); - - // Perform cleanup that cannot be performed in the destructor. - void Shutdown(); - - // Create a new provider. - EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue); - - // Delete a provider. - void DeleteProvider(EventPipeProvider *pProvider); - - // Register a provider. - bool RegisterProvider(EventPipeProvider &provider, EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue); - - // Unregister a provider. - bool UnregisterProvider(EventPipeProvider &provider); - - // Get the provider with the specified provider ID if it exists. - EventPipeProvider *GetProvider(const SString &providerID); - - // Enable a session in the event pipe. - void Enable( - EventPipeSession &session, - EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue); - - // Disable a session in the event pipe. - void Disable( - const EventPipeSession &session, - EventPipeProviderCallbackDataQueue *pEventPipeProviderCallbackDataQueue); - - // Get the event used to write metadata to the event stream. - EventPipeEventInstance *BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId); - - // Delete deferred providers. - void DeleteDeferredProviders(); - - // Compute the enabled bit mask, the ith bit is 1 iff an event with the given (provider, keywords, eventLevel) is enabled for the ith session. - INT64 ComputeEventEnabledMask(const EventPipeProvider& provider, INT64 keywords, EventPipeEventLevel eventLevel) const; -private: - // Get the provider without taking the lock. - EventPipeProvider *GetProviderNoLock(const SString &providerID); - - // Get the enabled provider. - EventPipeSessionProvider *GetSessionProvider(const EventPipeSession &session, const EventPipeProvider *pProvider) const; - - // Compute the keyword union and maximum level for a provider across all sessions - void ComputeKeywordAndLevel(const EventPipeProvider& provider, INT64& keywordsForAllSessions, EventPipeEventLevel& levelForAllSessions) const; - - // The list of event pipe providers. - SList> *m_pProviderList = nullptr; - - // The provider used to write configuration events to the event stream. - EventPipeProvider *m_pConfigProvider = nullptr; - - // The event used to write event information to the event stream. - EventPipeEvent *m_pMetadataEvent = nullptr; - - // The provider name for the configuration event pipe provider. - // This provider is used to emit configuration events. - const static WCHAR *s_configurationProviderName; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_CONFIGURATION_H__ diff --git a/src/coreclr/vm/eventpipeevent.cpp b/src/coreclr/vm/eventpipeevent.cpp deleted file mode 100644 index 79c56d8bef799..0000000000000 --- a/src/coreclr/vm/eventpipeevent.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeevent.h" -#include "eventpipeprovider.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeEvent::EventPipeEvent( - EventPipeProvider &provider, - INT64 keywords, - unsigned int eventID, - unsigned int eventVersion, - EventPipeEventLevel level, - bool needStack, - BYTE *pMetadata, - unsigned int metadataLength) : m_pProvider(&provider), - m_keywords(keywords), - m_eventID(eventID), - m_eventVersion(eventVersion), - m_level(level), - m_needStack(needStack), - m_enabledMask(0), - m_pMetadata(nullptr), - m_metadataLength(0) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(&provider != nullptr); - } - CONTRACTL_END; - - if (pMetadata != nullptr) - { - m_pMetadata = new BYTE[metadataLength]; - memcpy(m_pMetadata, pMetadata, metadataLength); - m_metadataLength = metadataLength; - } - else - { - // if metadata is not provided, we have to build the minimum version. It's required by the serialization contract - m_pMetadata = BuildMinimumMetadata(); - m_metadataLength = MinimumMetadataLength; - } -} - -EventPipeEvent::~EventPipeEvent() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - delete[] m_pMetadata; -} - -BYTE *EventPipeEvent::BuildMinimumMetadata() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - BYTE *minmumMetadata = new BYTE[MinimumMetadataLength]; - BYTE *currentPtr = minmumMetadata; - - // the order of fields is defined in EventSource.cs DefineEventPipeEvents method - memcpy(currentPtr, &m_eventID, sizeof(m_eventID)); - currentPtr += sizeof(m_eventID); - - SString eventName = SString::Empty(); - unsigned int eventNameSize = (eventName.GetCount() + 1) * sizeof(WCHAR); - memcpy(currentPtr, (BYTE *)eventName.GetUnicode(), eventNameSize); - currentPtr += eventNameSize; - - memcpy(currentPtr, &m_keywords, sizeof(m_keywords)); - currentPtr += sizeof(m_keywords); - - memcpy(currentPtr, &m_eventVersion, sizeof(m_eventVersion)); - currentPtr += sizeof(m_eventVersion); - - memcpy(currentPtr, &m_level, sizeof(m_level)); - currentPtr += sizeof(m_level); - - unsigned int noParameters = 0; - memcpy(currentPtr, &noParameters, sizeof(noParameters)); - currentPtr += sizeof(noParameters); - - return minmumMetadata; -} - -EventPipeProvider *EventPipeEvent::GetProvider() const -{ - LIMITED_METHOD_CONTRACT; - return m_pProvider; -} - -INT64 EventPipeEvent::GetKeywords() const -{ - LIMITED_METHOD_CONTRACT; - return m_keywords; -} - -unsigned int EventPipeEvent::GetEventID() const -{ - LIMITED_METHOD_CONTRACT; - return m_eventID; -} - -unsigned int EventPipeEvent::GetEventVersion() const -{ - LIMITED_METHOD_CONTRACT; - return m_eventVersion; -} - -EventPipeEventLevel EventPipeEvent::GetLevel() const -{ - LIMITED_METHOD_CONTRACT; - return m_level; -} - -bool EventPipeEvent::NeedStack() const -{ - LIMITED_METHOD_CONTRACT; - return m_needStack; -} - -bool EventPipeEvent::IsEnabled() const -{ - LIMITED_METHOD_CONTRACT; - return m_enabledMask != 0; -} - -BYTE *EventPipeEvent::GetMetadata() const -{ - LIMITED_METHOD_CONTRACT; - return m_pMetadata; -} - -unsigned int EventPipeEvent::GetMetadataLength() const -{ - LIMITED_METHOD_CONTRACT; - return m_metadataLength; -} - -void EventPipeEvent::RefreshState() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(EventPipe::IsLockOwnedByCurrentThread()); - m_enabledMask = m_pProvider->ComputeEventEnabledMask(m_keywords, m_level); -} - -bool EventPipeEvent::IsEnabled(uint64_t sessionMask) const -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pProvider != nullptr); - return (m_pProvider->IsEnabled(sessionMask) && (m_enabledMask & sessionMask) != 0); -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeevent.h b/src/coreclr/vm/eventpipeevent.h deleted file mode 100644 index b504e050956a5..0000000000000 --- a/src/coreclr/vm/eventpipeevent.h +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_EVENT_H__ -#define __EVENTPIPE_EVENT_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipeprovider.h" - -class EventPipeEvent -{ - // Declare friends. - friend class EventPipeProvider; - -private: - - // The provider that contains the event. - EventPipeProvider *const m_pProvider; - - // Bit vector containing the keywords that enable the event. - const INT64 m_keywords; - - // The ID (within the provider) of the event. - const unsigned int m_eventID; - - // The version of the event. - const unsigned int m_eventVersion; - - // The verbosity of the event. - const EventPipeEventLevel m_level; - - // True if a call stack should be captured when writing the event. - const bool m_needStack; - - // The ith bit is 1 iff the event is enabled for the ith session - Volatile m_enabledMask; - - // Metadata - BYTE *m_pMetadata; - - // Metadata length; - unsigned int m_metadataLength; - - // Refreshes the runtime state for this event. - // Called by EventPipeProvider when the provider configuration changes. - void RefreshState(); - - // Only EventPipeProvider can create events. - // The provider is responsible for allocating and freeing events. - EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, - BYTE *pMetadata = NULL, unsigned int metadataLength = 0); - -public: - ~EventPipeEvent(); - - // Get the provider associated with this event. - EventPipeProvider *GetProvider() const; - - // Get the keywords that enable the event. - INT64 GetKeywords() const; - - // Get the ID (within the provider) of the event. - unsigned int GetEventID() const; - - // Get the version of the event. - unsigned int GetEventVersion() const; - - // Get the verbosity of the event. - EventPipeEventLevel GetLevel() const; - - // True if a call stack should be captured when writing the event. - bool NeedStack() const; - - // True if the event is currently enabled. - bool IsEnabled() const; - - BYTE *GetMetadata() const; - - unsigned int GetMetadataLength() const; - - bool IsEnabled(uint64_t sessionMask) const; - -private: - // used when Metadata is not provided - BYTE *BuildMinimumMetadata(); - - static const uint32_t MinimumMetadataLength = - sizeof(m_eventID) + - sizeof(W("")) + // size of empty unicode string - sizeof(m_keywords) + - sizeof(m_eventVersion) + - sizeof(m_level) + - sizeof(uint32_t); // parameter count -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_EVENT_H__ diff --git a/src/coreclr/vm/eventpipeeventinstance.cpp b/src/coreclr/vm/eventpipeeventinstance.cpp deleted file mode 100644 index e60c3d5b0360c..0000000000000 --- a/src/coreclr/vm/eventpipeeventinstance.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeconfiguration.h" -#include "eventpipeeventinstance.h" -#include "eventpipejsonfile.h" -#include "fastserializer.h" -#include "sampleprofiler.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeEventInstance::EventPipeEventInstance( - EventPipeEvent &event, - unsigned int procNumber, - ULONGLONG threadId, - BYTE *pData, - unsigned int length, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifdef _DEBUG - m_debugEventStart = 0xDEADBEEF; - m_debugEventEnd = 0xCAFEBABE; -#endif // _DEBUG - m_pEvent = &event; - m_procNumber = procNumber; - m_threadId = threadId; - if (pActivityId != NULL) - { - m_activityId = *pActivityId; - } - else - { - m_activityId = {0}; - } - if (pRelatedActivityId != NULL) - { - m_relatedActivityId = *pRelatedActivityId; - } - else - { - m_relatedActivityId = {0}; - } - - m_pData = pData; - m_dataLength = length; - QueryPerformanceCounter(&m_timeStamp); - _ASSERTE(m_timeStamp.QuadPart > 0); -#ifdef _DEBUG - EnsureConsistency(); -#endif // _DEBUG -} - -void EventPipeEventInstance::EnsureStack(const EventPipeSession &session) -{ - if (m_pEvent->NeedStack() && !session.RundownEnabled()) - { - EventPipe::WalkManagedStackForCurrentThread(m_stackContents); - } -} - -unsigned int EventPipeEventInstance::GetAlignedTotalSize(EventPipeSerializationFormat format) const -{ - CONTRACT(unsigned int) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - POSTCONDITION(RETVAL % ALIGNMENT_SIZE == 0); - } - CONTRACT_END; - - // Calculate the size of the total payload so that it can be written to the file. - unsigned int payloadLength = 0; - - if (format == EventPipeSerializationFormat::NetPerfV3) - { - payloadLength = - sizeof(m_metadataId) + // Metadata ID - sizeof(DWORD) + // Thread ID - sizeof(m_timeStamp) + // TimeStamp - sizeof(m_activityId) + // Activity ID - sizeof(m_relatedActivityId) + // Related Activity ID - sizeof(m_dataLength) + // Data payload length - m_dataLength + // Event payload data - sizeof(unsigned int) + // Prepended stack payload size in bytes - m_stackContents.GetSize(); // Stack payload size - } - else if (format == EventPipeSerializationFormat::NetTraceV4) - { - payloadLength = - sizeof(m_metadataId) + // Metadata ID - sizeof(unsigned int) + // Sequence number (implied by the buffer containing the event instance) - sizeof(m_threadId) + // Thread ID - sizeof(ULONGLONG) + // Capture Thread ID (implied by the buffer containing the event instance) - sizeof(m_procNumber) + // ProcNumber - sizeof(unsigned int) + // Stack intern table id - sizeof(m_timeStamp) + // TimeStamp - sizeof(m_activityId) + // Activity ID - sizeof(m_relatedActivityId) + // Related Activity ID - sizeof(m_dataLength) + // Data payload length - m_dataLength; // Event payload data - } - else - { - _ASSERTE(!"Unrecognized format"); - } - - - // round up to ALIGNMENT_SIZE bytes - if (payloadLength % ALIGNMENT_SIZE != 0) - { - payloadLength += ALIGNMENT_SIZE - (payloadLength % ALIGNMENT_SIZE); - } - - RETURN payloadLength; -} - -#ifdef _DEBUG -void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(pFile == NULL) - { - return; - } - - EX_TRY - { - StackScratchBuffer scratch; - SString providerName = m_pEvent->GetProvider()->GetProviderName(); - - SString message; - message.Printf("Provider=%s/EventID=%d/Version=%d", providerName.GetANSI(scratch), m_pEvent->GetEventID(), m_pEvent->GetEventVersion()); - pFile->WriteEvent(m_timeStamp, (DWORD)m_threadId, message, m_stackContents); - } - EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); -} -#endif - -void EventPipeEventInstance::SetTimeStamp(LARGE_INTEGER timeStamp) -{ - LIMITED_METHOD_CONTRACT; - - m_timeStamp = timeStamp; -} - -#ifdef _DEBUG -bool EventPipeEventInstance::EnsureConsistency() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Validate event start. - _ASSERTE(m_debugEventStart == 0xDEADBEEF); - - // Validate event end. - _ASSERTE(m_debugEventEnd == 0xCAFEBABE); - - return true; -} -#endif // _DEBUG - -EventPipeSequencePoint::EventPipeSequencePoint() -{ - LIMITED_METHOD_CONTRACT; - TimeStamp.QuadPart = 0; -} - -EventPipeSequencePoint::~EventPipeSequencePoint() -{ - // Each entry in the map owns a ref-count on the corresponding thread - for (ThreadSequenceNumberMap::Iterator pCur = ThreadSequenceNumbers.Begin(); - pCur != ThreadSequenceNumbers.End(); - pCur++) - { - pCur->Key()->GetThread()->Release(); - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeeventinstance.h b/src/coreclr/vm/eventpipeeventinstance.h deleted file mode 100644 index 64e6e385ac0d6..0000000000000 --- a/src/coreclr/vm/eventpipeeventinstance.h +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_EVENTINSTANCE_H__ -#define __EVENTPIPE_EVENTINSTANCE_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipeevent.h" -#include "eventpipesession.h" -#include "eventpipeblock.h" -#include "eventpipethread.h" -#include "fastserializableobject.h" -#include "fastserializer.h" - -class EventPipeJsonFile; - -class EventPipeEventInstance -{ - // Declare friends. - friend EventPipeConfiguration; - -public: - - EventPipeEventInstance(EventPipeEvent &event, - unsigned int procNumber, - ULONGLONG threadID, - BYTE *pData, - unsigned int length, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId); - - void EnsureStack(const EventPipeSession &session); - - StackContents* GetStack() - { - LIMITED_METHOD_CONTRACT; - - return &m_stackContents; - } - - EventPipeEvent* GetEvent() const - { - LIMITED_METHOD_CONTRACT; - - return m_pEvent; - } - - const LARGE_INTEGER* GetTimeStamp() const - { - LIMITED_METHOD_CONTRACT; - - return &m_timeStamp; - } - - unsigned int GetMetadataId() const - { - LIMITED_METHOD_CONTRACT; - - return m_metadataId; - } - - void SetMetadataId(unsigned int metadataId) - { - LIMITED_METHOD_CONTRACT; - - m_metadataId = metadataId; - } - - unsigned int GetProcNumber() const - { - LIMITED_METHOD_CONTRACT; - - return m_procNumber; - } - - DWORD GetThreadId32() const - { - LIMITED_METHOD_CONTRACT; - - return (DWORD)m_threadId; - } - - ULONGLONG GetThreadId64() const - { - LIMITED_METHOD_CONTRACT; - - return m_threadId; - } - - const GUID* GetActivityId() const - { - LIMITED_METHOD_CONTRACT; - - return &m_activityId; - } - - const GUID* GetRelatedActivityId() const - { - LIMITED_METHOD_CONTRACT; - - return &m_relatedActivityId; - } - - const BYTE* GetData() const - { - LIMITED_METHOD_CONTRACT; - - return m_pData; - } - - unsigned int GetDataLength() const - { - LIMITED_METHOD_CONTRACT; - - return m_dataLength; - } - - unsigned int GetStackSize() const - { - LIMITED_METHOD_CONTRACT; - - return m_stackContents.GetSize(); - } - - unsigned int GetAlignedTotalSize(EventPipeSerializationFormat format) const; - -#ifdef _DEBUG - // Serialize this event to the JSON file. - void SerializeToJsonFile(EventPipeJsonFile *pFile); - - bool EnsureConsistency(); -#endif // _DEBUG - -protected: - -#ifdef _DEBUG - unsigned int m_debugEventStart; -#endif // _DEBUG - - EventPipeEvent *m_pEvent; - unsigned int m_metadataId; - unsigned int m_procNumber; - ULONGLONG m_threadId; - LARGE_INTEGER m_timeStamp; - GUID m_activityId; - GUID m_relatedActivityId; - - BYTE *m_pData; - unsigned int m_dataLength; - StackContents m_stackContents; - -#ifdef _DEBUG - unsigned int m_debugEventEnd; -#endif // _DEBUG - -private: - - // This is used for metadata events by EventPipeConfiguration because - // the metadata event is created after the first instance of the event - // but must be inserted into the file before the first instance of the event. - void SetTimeStamp(LARGE_INTEGER timeStamp); -}; - -typedef MapSHash ThreadSequenceNumberMap; - -// A point in time marker that is used as a boundary when emitting events. -// The events in a Nettrace file are not emitted in a fully sorted order -// but we do guarantee that all events before a sequence point are emitted -// prior to any events after the sequence point -struct EventPipeSequencePoint -{ - // Entry in EventPipeBufferManager m_sequencePointList - SLink m_Link; - - // The timestamp the sequence point was captured - LARGE_INTEGER TimeStamp; - ThreadSequenceNumberMap ThreadSequenceNumbers; - - EventPipeSequencePoint(); - ~EventPipeSequencePoint(); -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_EVENTINSTANCE_H__ diff --git a/src/coreclr/vm/eventpipeeventpayload.cpp b/src/coreclr/vm/eventpipeeventpayload.cpp deleted file mode 100644 index 7a925aef3a378..0000000000000 --- a/src/coreclr/vm/eventpipeeventpayload.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeeventpayload.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_pData = NULL; - m_pEventData = pEventData; - m_eventDataCount = eventDataCount; - m_allocatedData = false; - - S_UINT32 tmp_size = S_UINT32(0); - for (unsigned int i = 0; i < m_eventDataCount; i++) - { - tmp_size += S_UINT32(m_pEventData[i].Size); - } - - if (tmp_size.IsOverflow()) - { - // If there is an overflow, drop the data and create an empty payload - m_pEventData = NULL; - m_eventDataCount = 0; - m_size = 0; - } - else - { - m_size = tmp_size.Value(); - } -} - -EventPipeEventPayload::~EventPipeEventPayload() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_allocatedData && m_pData != NULL) - { - delete[] m_pData; - m_pData = NULL; - } -} - -void EventPipeEventPayload::Flatten() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_size > 0) - { - if (!IsFlattened()) - { - BYTE *tmp_pData = new (nothrow) BYTE[m_size]; - if (tmp_pData != NULL) - { - m_allocatedData = true; - CopyData(tmp_pData); - m_pData = tmp_pData; - } - } - } -} - -void EventPipeEventPayload::CopyData(BYTE *pDst) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_size > 0) - { - if (IsFlattened()) - { - memcpy(pDst, m_pData, m_size); - } - - else if (m_pEventData != NULL) - { - unsigned int offset = 0; - for (unsigned int i = 0; i < m_eventDataCount; i++) - { - memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size); - offset += m_pEventData[i].Size; - } - } - } -} - -BYTE *EventPipeEventPayload::GetFlatData() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (!IsFlattened()) - { - Flatten(); - } - return m_pData; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeeventpayload.h b/src/coreclr/vm/eventpipeeventpayload.h deleted file mode 100644 index 57c2e74b9c826..0000000000000 --- a/src/coreclr/vm/eventpipeeventpayload.h +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_EVENTPAYLOAD_H__ -#define __EVENTPIPE_EVENTPAYLOAD_H__ - -#ifdef FEATURE_PERFTRACING -#include "common.h" - -struct EventData -{ - UINT64 Ptr; - unsigned int Size; - unsigned int Reserved; -}; - -class EventPipeEventPayload -{ -private: - BYTE *m_pData; - EventData *m_pEventData; - unsigned int m_eventDataCount; - unsigned int m_size; - bool m_allocatedData; - - // If the data is stored only as an array of EventData objects, create a flat buffer and copy into it - void Flatten(); - -public: - // Build this payload with a flat buffer inside - EventPipeEventPayload(BYTE *pData, unsigned int length) : - m_pData(pData), - m_pEventData(nullptr), - m_eventDataCount(0), - m_size(length), - m_allocatedData(false) - { - LIMITED_METHOD_CONTRACT; - } - - // Build this payload to contain an array of EventData objects - EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount); - - // If a buffer was allocated internally, delete it - ~EventPipeEventPayload(); - - // Copy the data (whether flat or array of objects) into a flat buffer at pDst - // Assumes that pDst points to an appropriatly sized buffer - void CopyData(BYTE *pDst); - - // Get the flat formatted data in this payload - // This method will allocate a buffer if it does not already contain flattened data - // This method will return NULL on OOM if a buffer needed to be allocated - BYTE *GetFlatData(); - - // Return true is the data is stored in a flat buffer - bool IsFlattened() const - { - LIMITED_METHOD_CONTRACT; - return m_pData != NULL; - } - - // The the size of buffer needed to contain the stored data - unsigned int GetSize() const - { - LIMITED_METHOD_CONTRACT; - return m_size; - } - - EventData *GetEventDataArray() const - { - LIMITED_METHOD_CONTRACT; - return m_pEventData; - } -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_EVENTPAYLOAD_H__ diff --git a/src/coreclr/vm/eventpipeeventsource.cpp b/src/coreclr/vm/eventpipeeventsource.cpp deleted file mode 100644 index 0febf373a6e13..0000000000000 --- a/src/coreclr/vm/eventpipeeventsource.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeeventpayload.h" -#include "eventpipeeventsource.h" -#include "eventpipe.h" -#include "eventpipeevent.h" -#include "eventpipemetadatagenerator.h" -#include "eventpipeprovider.h" -#include "eventpipesession.h" -#include "eventpipesessionprovider.h" - -#ifdef FEATURE_PERFTRACING - -const WCHAR* EventPipeEventSource::s_pProviderName = W("Microsoft-DotNETCore-EventPipe"); -const WCHAR* EventPipeEventSource::s_pProcessInfoEventName = W("ProcessInfo"); - -#if defined(HOST_WINDOWS) -const WCHAR* EventPipeEventSource::s_pOSInformation = W("Windows"); -#elif defined(__APPLE__) -const WCHAR* EventPipeEventSource::s_pOSInformation = W("macOS"); -#elif defined(__linux__) -const WCHAR* EventPipeEventSource::s_pOSInformation = W("Linux"); -#else -const WCHAR* EventPipeEventSource::s_pOSInformation = W("Unknown"); -#endif - -#if defined(TARGET_X86) -const WCHAR* EventPipeEventSource::s_pArchInformation = W("x86"); -#elif defined(TARGET_AMD64) -const WCHAR* EventPipeEventSource::s_pArchInformation = W("x64"); -#elif defined(TARGET_ARM) -const WCHAR* EventPipeEventSource::s_pArchInformation = W("arm32"); -#elif defined(TARGET_ARM64) -const WCHAR* EventPipeEventSource::s_pArchInformation = W("arm64"); -#else -const WCHAR* EventPipeEventSource::s_pArchInformation = W("Unknown"); -#endif - -EventPipeEventSource::EventPipeEventSource() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - m_pProvider = EventPipe::CreateProvider(SL(s_pProviderName), NULL, NULL); - - // Generate metadata. - const unsigned int numParams = 3; - EventPipeParameterDesc params[numParams]; - params[0].Type = EventPipeParameterType::String; - params[0].Name = W("CommandLine"); - params[1].Type = EventPipeParameterType::String; - params[1].Name = W("OSInformation"); - params[2].Type = EventPipeParameterType::String; - params[2].Name = W("ArchInformation"); - - size_t metadataLength = 0; - BYTE *pMetadata = EventPipeMetadataGenerator::GenerateEventMetadata( - 1, /* eventID */ - s_pProcessInfoEventName, - 0, /* keywords */ - 1, /* version */ - EventPipeEventLevel::LogAlways, - 0, /* opcode */ - params, - numParams, - &metadataLength); - - // Add the event. - m_pProcessInfoEvent = m_pProvider->AddEvent( - 1, /* eventID */ - 0, /* keywords */ - 0, /* eventVersion */ - EventPipeEventLevel::LogAlways, - false, /* needStack */ - pMetadata, - (unsigned int)metadataLength); - - // Delete the metadata after the event is created. - // The metadata blob will be copied into EventPipe-owned memory. - delete [] pMetadata; -} - -EventPipeEventSource::~EventPipeEventSource() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Delete the provider and associated events. - // This is called in the shutdown path which can't throw. - // Catch exceptions and ignore failures. - EX_TRY - { - EventPipe::DeleteProvider(m_pProvider); - } - EX_CATCH { } - EX_END_CATCH(SwallowAllExceptions); -} - -void EventPipeEventSource::Enable(EventPipeSession *pSession) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pSession != NULL); - } - CONTRACTL_END; - - if (pSession == nullptr) - return; - - pSession->AddSessionProvider(new EventPipeSessionProvider( - s_pProviderName, - static_cast(-1), - EventPipeEventLevel::LogAlways, - NULL)); -} - -void EventPipeEventSource::SendProcessInfo(LPCWSTR pCommandLine) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - EventData data[3]; - data[0].Ptr = (UINT64) pCommandLine; - data[0].Size = (unsigned int)(wcslen(pCommandLine) + 1) * 2; - data[0].Reserved = 0; - data[1].Ptr = (UINT64)s_pOSInformation; - data[1].Size = (unsigned int)(wcslen(s_pOSInformation) + 1) * 2; - data[1].Reserved = 0; - data[2].Ptr = (UINT64)s_pArchInformation; - data[2].Size = (unsigned int)(wcslen(s_pArchInformation) + 1) * 2; - data[2].Reserved = 0; - - EventPipe::WriteEvent(*m_pProcessInfoEvent, data, 3); -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeeventsource.h b/src/coreclr/vm/eventpipeeventsource.h deleted file mode 100644 index 9100cbdef841f..0000000000000 --- a/src/coreclr/vm/eventpipeeventsource.h +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_EVENTSOURCE_H__ -#define __EVENTPIPE_EVENTSOURCE_H__ - -#ifdef FEATURE_PERFTRACING - -class EventPipeProvider; -class EventPipeEvent; -class EventPipeSession; - -class EventPipeEventSource -{ -private: - const static WCHAR* s_pProviderName; - EventPipeProvider *m_pProvider; - - const static WCHAR* s_pProcessInfoEventName; - EventPipeEvent *m_pProcessInfoEvent; - -public: - EventPipeEventSource(); - ~EventPipeEventSource(); - - void Enable(EventPipeSession *pSession); - void SendProcessInfo(LPCWSTR pCommandLine); - - const static WCHAR* s_pOSInformation; - const static WCHAR* s_pArchInformation; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_EVENTSOURCE_H__ diff --git a/src/coreclr/vm/eventpipefile.cpp b/src/coreclr/vm/eventpipefile.cpp deleted file mode 100644 index c3388c0f464cb..0000000000000 --- a/src/coreclr/vm/eventpipefile.cpp +++ /dev/null @@ -1,484 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeblock.h" -#include "eventpipeeventinstance.h" -#include "eventpipefile.h" -#include "sampleprofiler.h" - -#ifdef FEATURE_PERFTRACING - - -StackHashEntry* StackHashEntry::CreateNew(StackContents* pStack, ULONG id, ULONG hash) -{ - LIMITED_METHOD_CONTRACT; - - StackHashEntry* pEntry = (StackHashEntry*) new (nothrow) BYTE[offsetof(StackHashEntry, StackBytes) + pStack->GetSize()]; - if (pEntry == NULL) - { - return NULL; - } - pEntry->Id = id; - pEntry->Hash = hash; - pEntry->StackSizeInBytes = pStack->GetSize(); - memcpy_s(pEntry->StackBytes, pStack->GetSize(), pStack->GetPointer(), pStack->GetSize()); - return pEntry; -} - -StackHashKey StackHashEntry::GetKey() const -{ - LIMITED_METHOD_CONTRACT; - StackHashKey key((BYTE*)StackBytes, StackSizeInBytes, Hash); - return key; -} - -StackHashKey::StackHashKey(StackContents* pStack) : - pStackBytes(pStack->GetPointer()), - Hash(HashBytes(pStack->GetPointer(), pStack->GetSize())), - StackSizeInBytes(pStack->GetSize()) -{} - -StackHashKey::StackHashKey(BYTE* pStackBytes, ULONG stackSizeInBytes, ULONG hash) : - pStackBytes(pStackBytes), - Hash(hash), - StackSizeInBytes(stackSizeInBytes) -{} - -DWORD GetFileVersion(EventPipeSerializationFormat format) -{ - LIMITED_METHOD_CONTRACT; - switch(format) - { - case EventPipeSerializationFormat::NetPerfV3: - return 3; - case EventPipeSerializationFormat::NetTraceV4: - return 4; - default: - _ASSERTE(!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - -DWORD GetFileMinVersion(EventPipeSerializationFormat format) -{ - LIMITED_METHOD_CONTRACT; - switch (format) - { - case EventPipeSerializationFormat::NetPerfV3: - return 0; - case EventPipeSerializationFormat::NetTraceV4: - return 4; - default: - _ASSERTE(!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - -EventPipeFile::EventPipeFile(StreamWriter *pStreamWriter, EventPipeSerializationFormat format) : - FastSerializableObject(GetFileVersion(format), GetFileMinVersion(format), format >= EventPipeSerializationFormat::NetTraceV4), - m_pSerializer(nullptr), - m_pStreamWriter(pStreamWriter) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - m_format = format; - m_pBlock = new EventPipeEventBlock(100 * 1024, format); - m_pMetadataBlock = new EventPipeMetadataBlock(100 * 1024); - m_pStackBlock = new EventPipeStackBlock(100 * 1024); - - // File start time information. - GetSystemTime(&m_fileOpenSystemTime); - QueryPerformanceCounter(&m_fileOpenTimeStamp); - QueryPerformanceFrequency(&m_timeStampFrequency); - - m_pointerSize = TARGET_POINTER_SIZE; - - m_currentProcessId = GetCurrentProcessId(); - - SYSTEM_INFO sysinfo = {}; - GetSystemInfo(&sysinfo); - m_numberOfProcessors = sysinfo.dwNumberOfProcessors; - - m_samplingRateInNs = SampleProfiler::GetSamplingRate(); - - - - m_serializationLock.Init(LOCK_TYPE_DEFAULT); - - m_pMetadataIds = new MapSHashWithRemove(); - - // Start at 0 - The value is always incremented prior to use, so the first ID will be 1. - m_metadataIdCounter = 0; - - // Start at 0 - The value is always incremented prior to use, so the first ID will be 1. - m_stackIdCounter = 0; - -#ifdef DEBUG - QueryPerformanceCounter(&m_lastSortedTimestamp); -#endif - - -} - -void EventPipeFile::InitializeFile() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(m_pStreamWriter != nullptr); - PRECONDITION(m_pSerializer == nullptr); - } - CONTRACTL_END; - - bool fSuccess = true; - if (m_format >= EventPipeSerializationFormat::NetTraceV4) - { - const char* pHeader = "Nettrace"; - uint32_t bytesWritten = 0; - fSuccess = m_pStreamWriter->Write(pHeader, 8, bytesWritten) && bytesWritten == 8; - } - if (fSuccess) - { - m_isInitialized = fSuccess; - // Create the file stream and write the FastSerialization header. - m_pSerializer = new FastSerializer(m_pStreamWriter); - - // Write the first object to the file. - m_pSerializer->WriteObject(this); - } -} - -EventPipeFile::~EventPipeFile() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pBlock != NULL && m_pSerializer != NULL) - WriteEnd(); - - for (EventPipeStackHash::Iterator pCur = m_stackHash.Begin(); pCur != m_stackHash.End(); pCur++) - { - delete *pCur; - } - - if (!m_isInitialized) - delete m_pStreamWriter; - - delete m_pBlock; - delete m_pMetadataBlock; - delete m_pStackBlock; - delete m_pSerializer; - delete m_pMetadataIds; -} - -EventPipeSerializationFormat EventPipeFile::GetSerializationFormat() const -{ - LIMITED_METHOD_CONTRACT; - return m_format; -} - -bool EventPipeFile::HasErrors() const -{ - LIMITED_METHOD_CONTRACT; - return (m_pSerializer == nullptr) || m_pSerializer->HasWriteErrors(); -} - -void EventPipeFile::WriteEvent(EventPipeEventInstance &instance, ULONGLONG captureThreadId, unsigned int sequenceNumber, BOOL isSortedEvent) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (HasErrors()) return; - -#ifdef DEBUG - _ASSERTE(instance.GetTimeStamp()->QuadPart >= m_lastSortedTimestamp.QuadPart); - if (isSortedEvent) - { - m_lastSortedTimestamp = *(instance.GetTimeStamp()); - } -#endif - - unsigned int stackId = 0; - if (m_format >= EventPipeSerializationFormat::NetTraceV4) - { - stackId = GetStackId(instance); - } - - // Check to see if we've seen this event type before. - // If not, then write the event metadata to the event stream first. - EventPipeEvent* pEvent = instance.GetEvent(); - unsigned int metadataId = GetMetadataId(*pEvent); - if(metadataId == 0) - { - metadataId = GenerateMetadataId(); - - EventPipeEventInstance* pMetadataInstance = EventPipe::BuildEventMetadataEvent(instance, metadataId); - - WriteEventToBlock(*pMetadataInstance, 0); // metadataId=0 breaks recursion and represents the metadata event. - - SaveMetadataId(*instance.GetEvent(), metadataId); - - delete[] pMetadataInstance->GetData(); - delete pMetadataInstance; - } - - WriteEventToBlock(instance, metadataId, captureThreadId, sequenceNumber, stackId, isSortedEvent); -} - -void EventPipeFile::WriteSequencePoint(EventPipeSequencePoint* pSequencePoint) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pSequencePoint != nullptr); - } - CONTRACTL_END; - - if (m_format < EventPipeSerializationFormat::NetTraceV4) - { - // sequence points aren't used in NetPerf format - return; - } - - Flush(FlushAllBlocks); - EventPipeSequencePointBlock sequencePointBlock(pSequencePoint); - - if (HasErrors()) return; - - m_pSerializer->WriteObject(&sequencePointBlock); - - // stack cache resets on sequence points - m_stackIdCounter = 0; - for (EventPipeStackHash::Iterator pCur = m_stackHash.Begin(); pCur != m_stackHash.End(); pCur++) - { - delete *pCur; - } - m_stackHash.RemoveAll(); -} - -void EventPipeFile::Flush(FlushFlags flags) -{ - // Write existing buffer to the stream/file regardless of whether it is full or not. - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_pMetadataBlock != nullptr); - PRECONDITION(m_pStackBlock != nullptr); - PRECONDITION(m_pBlock != nullptr); - } - CONTRACTL_END; - - if (HasErrors()) return; - - // we write current blocks to the disk, whether they are full or not - if ((m_pMetadataBlock->GetBytesWritten() != 0) && ((flags & FlushMetadataBlock) != 0)) - { - _ASSERTE(m_format >= EventPipeSerializationFormat::NetTraceV4); - m_pSerializer->WriteObject(m_pMetadataBlock); - m_pMetadataBlock->Clear(); - } - if ((m_pStackBlock->GetBytesWritten() != 0) && ((flags & FlushStackBlock) != 0)) - { - _ASSERTE(m_format >= EventPipeSerializationFormat::NetTraceV4); - m_pSerializer->WriteObject(m_pStackBlock); - m_pStackBlock->Clear(); - } - if ((m_pBlock->GetBytesWritten() != 0) && ((flags & FlushEventBlock) != 0)) - { - m_pSerializer->WriteObject(m_pBlock); - m_pBlock->Clear(); - } -} - -void EventPipeFile::WriteEnd() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_pSerializer != nullptr); - } - CONTRACTL_END; - - Flush(); - - // "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read." - // see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more - m_pSerializer->WriteTag(FastSerializerTags::NullReference); -} - -void EventPipeFile::WriteEventToBlock(EventPipeEventInstance &instance, - unsigned int metadataId, - ULONGLONG captureThreadId, - unsigned int sequenceNumber, - unsigned int stackId, - BOOL isSortedEvent) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_pBlock != nullptr); - PRECONDITION(m_pMetadataBlock != nullptr); - } - CONTRACTL_END; - - instance.SetMetadataId(metadataId); - - // If we are flushing events we need to flush metadata and stacks as well - // to ensure referenced metadata/stacks were written to the file before the - // event which referenced them. - FlushFlags flags = FlushAllBlocks; - EventPipeEventBlockBase* pBlock = m_pBlock; - if(metadataId == 0 && m_format >= EventPipeSerializationFormat::NetTraceV4) - { - flags = FlushMetadataBlock; - pBlock = m_pMetadataBlock; - } - - if (pBlock->WriteEvent(instance, captureThreadId, sequenceNumber, stackId, isSortedEvent)) - return; // the block is not full, we added the event and continue - - // we can't write this event to the current block (it's full) - // so we write what we have in the block to the serializer - Flush(flags); - - bool result = pBlock->WriteEvent(instance, captureThreadId, sequenceNumber, stackId, isSortedEvent); - - _ASSERTE(result == true); // we should never fail to add event to a clear block (if we do the max size is too small) -} - -unsigned int EventPipeFile::GenerateMetadataId() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // PAL does not support 32 bit InterlockedIncrement, so we are using the LONG version and cast to int - // https://github.com/dotnet/coreclr/blob/master/src/pal/inc/pal.h#L4159 - // it's ok because the metadataId will never be bigger than 32 bit - return (unsigned int)InterlockedIncrement(&m_metadataIdCounter); -} - -unsigned int EventPipeFile::GetMetadataId(EventPipeEvent &event) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_pMetadataIds != nullptr); - } - CONTRACTL_END; - - unsigned int metadataId; - if(m_pMetadataIds->Lookup(&event, &metadataId)) - { - _ASSERTE(metadataId != 0); - return metadataId; - } - - return 0; -} - -void EventPipeFile::SaveMetadataId(EventPipeEvent &event, unsigned int metadataId) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(metadataId > 0); - PRECONDITION(m_pMetadataIds != nullptr); - } - CONTRACTL_END; - - // If a pre-existing metadata label exists, remove it. - unsigned int oldId; - if(m_pMetadataIds->Lookup(&event, &oldId)) - m_pMetadataIds->Remove(&event); - - // Add the metadata label. - m_pMetadataIds->Add(&event, metadataId); -} - -unsigned int EventPipeFile::GetStackId(EventPipeEventInstance &instance) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_format >= EventPipeSerializationFormat::NetTraceV4); - PRECONDITION(m_pStackBlock != nullptr); - } - CONTRACTL_END; - - unsigned int stackId = 0; - StackHashEntry* pEntry = NULL; - StackHashKey key(instance.GetStack()); - if (NULL == (pEntry = m_stackHash.Lookup(key))) - { - stackId = ++m_stackIdCounter; - - pEntry = StackHashEntry::CreateNew(instance.GetStack(), stackId, key.Hash); - if (pEntry != NULL) - { - EX_TRY - { - m_stackHash.Add(pEntry); - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions); - } - - if (m_pStackBlock->WriteStack(stackId, instance.GetStack())) - return stackId; - - // we can't write this stack to the current block (it's full) - // so we write what we have in the block to the serializer - Flush(FlushStackBlock); - - bool result = m_pStackBlock->WriteStack(stackId, instance.GetStack()); - _ASSERTE(result == true); // we should never fail to add event to a clear block (if we do the max size is too small) - } - else - { - stackId = pEntry->Id; - } - - return stackId; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipefile.h b/src/coreclr/vm/eventpipefile.h deleted file mode 100644 index f284fabf0f931..0000000000000 --- a/src/coreclr/vm/eventpipefile.h +++ /dev/null @@ -1,186 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_FILE_H__ -#define __EVENTPIPE_FILE_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipeblock.h" -#include "fastserializableobject.h" - -class EventPipeConfiguration; -class EventPipeEventInstance; -class FastSerializer; -struct EventPipeSequencePoint; - -struct StackHashKey -{ - BYTE* pStackBytes; - ULONG Hash; - ULONG StackSizeInBytes; - - StackHashKey(StackContents* pStack); - StackHashKey(BYTE* pStack, ULONG stackSizeInBytes, ULONG hash); -}; - -struct StackHashEntry -{ - ULONG Id; - ULONG Hash; - ULONG StackSizeInBytes; - // This is the first byte of StackSizeInBytes bytes of stack data - BYTE StackBytes[1]; - - static StackHashEntry* CreateNew(StackContents* pStack, ULONG id, ULONG hash); - StackHashKey GetKey() const; -}; - -class EventPipeStackHashTraits : public NoRemoveSHashTraits> -{ -public: - typedef typename DefaultSHashTraits::element_t element_t; - typedef typename DefaultSHashTraits::count_t count_t; - - typedef const StackHashKey key_t; - - static key_t GetKey(element_t e) - { - LIMITED_METHOD_CONTRACT; - return e->GetKey(); - } - static BOOL Equals(key_t k1, key_t k2) - { - LIMITED_METHOD_CONTRACT; - return k1.StackSizeInBytes == k2.StackSizeInBytes && - memcmp(k1.pStackBytes, k2.pStackBytes, k1.StackSizeInBytes) == 0; - } - static count_t Hash(key_t k) - { - LIMITED_METHOD_CONTRACT; - return (count_t)(size_t)k.Hash; - } - - static element_t Null() { LIMITED_METHOD_CONTRACT; return nullptr; } - static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == nullptr; } -}; - -typedef SHash EventPipeStackHash; - -class EventPipeFile final : public FastSerializableObject -{ -public: - EventPipeFile(StreamWriter *pStreamWriter, EventPipeSerializationFormat format); - ~EventPipeFile(); - - void InitializeFile(); - EventPipeSerializationFormat GetSerializationFormat() const; - void WriteEvent(EventPipeEventInstance &instance, ULONGLONG captureThreadId, unsigned int sequenceNumber, BOOL isSortedEvent); - void WriteSequencePoint(EventPipeSequencePoint* pSequencePoint); - enum FlushFlags - { - FlushEventBlock = 1, - FlushMetadataBlock = 2, - FlushStackBlock = 4, - FlushAllBlocks = FlushEventBlock | FlushMetadataBlock | FlushStackBlock - }; - void Flush(FlushFlags flags = FlushAllBlocks); - bool HasErrors() const; - - const char *GetTypeName() override - { - LIMITED_METHOD_CONTRACT; - return "Trace"; - } - - void FastSerialize(FastSerializer *pSerializer) override - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pSerializer != NULL); - } - CONTRACTL_END; - - pSerializer->WriteBuffer((BYTE *)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime)); - pSerializer->WriteBuffer((BYTE *)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp)); - pSerializer->WriteBuffer((BYTE *)&m_timeStampFrequency, sizeof(m_timeStampFrequency)); - - // the beginning of V3 - pSerializer->WriteBuffer((BYTE *)&m_pointerSize, sizeof(m_pointerSize)); - pSerializer->WriteBuffer((BYTE *)&m_currentProcessId, sizeof(m_currentProcessId)); - pSerializer->WriteBuffer((BYTE *)&m_numberOfProcessors, sizeof(m_numberOfProcessors)); - pSerializer->WriteBuffer((BYTE *)&m_samplingRateInNs, sizeof(m_samplingRateInNs)); - } - -private: - void WriteEnd(); - - unsigned int GenerateMetadataId(); - - unsigned int GetMetadataId(EventPipeEvent &event); - - unsigned int GetStackId(EventPipeEventInstance &instance); - - void SaveMetadataId(EventPipeEvent &event, unsigned int metadataId); - - void WriteEventToBlock(EventPipeEventInstance &instance, - unsigned int metadataId, - ULONGLONG captureThreadId = 0, - unsigned int sequenceNumber = 0, - unsigned int stackId = 0, - BOOL isSortedEvent = TRUE); - - // The format to serialize - EventPipeSerializationFormat m_format; - - // The object responsible for serialization. - FastSerializer *m_pSerializer; - - EventPipeEventBlock *m_pBlock; - EventPipeMetadataBlock *m_pMetadataBlock; - EventPipeStackBlock *m_pStackBlock; - - // The system time when the file was opened. - SYSTEMTIME m_fileOpenSystemTime; - - // The timestamp when the file was opened. Used for calculating file-relative timestamps. - LARGE_INTEGER m_fileOpenTimeStamp; - - // The frequency of the timestamps used for this file. - LARGE_INTEGER m_timeStampFrequency; - - StreamWriter * const m_pStreamWriter; - - unsigned int m_pointerSize; - - unsigned int m_currentProcessId; - - unsigned int m_numberOfProcessors; - - unsigned int m_samplingRateInNs; - - // The serialization which is responsible for making sure only a single event - // or block of events gets written to the file at once. - SpinLock m_serializationLock; - - // Hashtable of metadata labels. - MapSHashWithRemove *m_pMetadataIds; - - Volatile m_metadataIdCounter; - - Volatile m_isInitialized = false; - - unsigned int m_stackIdCounter; - EventPipeStackHash m_stackHash; -#ifdef DEBUG - LARGE_INTEGER m_lastSortedTimestamp; -#endif -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_FILE_H__ diff --git a/src/coreclr/vm/eventpipejsonfile.cpp b/src/coreclr/vm/eventpipejsonfile.cpp deleted file mode 100644 index 864d60bc64bb8..0000000000000 --- a/src/coreclr/vm/eventpipejsonfile.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipejsonfile.h" -#include "typestring.h" - -#ifdef _DEBUG -#ifdef FEATURE_PERFTRACING - -EventPipeJsonFile::EventPipeJsonFile(SString &outFilePath) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - m_writeErrorEncountered = false; - m_pFileStream = new CFileStream(); - if(FAILED(m_pFileStream->OpenForWrite(outFilePath))) - { - delete(m_pFileStream); - m_pFileStream = NULL; - return; - } - - QueryPerformanceCounter(&m_fileOpenTimeStamp); - - SString fileStart(W("{\n\"StackSource\" : {\n\"Samples\" : [\n")); - Write(fileStart); -} - -EventPipeJsonFile::~EventPipeJsonFile() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pFileStream != NULL) - { - if(!m_writeErrorEncountered) - { - SString closingString(W("]}}")); - Write(closingString); - } - - delete(m_pFileStream); - m_pFileStream = NULL; - } -} - -void EventPipeJsonFile::WriteEvent(EventPipeEventInstance &instance) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - instance.SerializeToJsonFile(this); -} - -void EventPipeJsonFile::WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pFileStream == NULL || m_writeErrorEncountered) - { - return; - } - - // Format the call stack. - SString strCallStack; - FormatCallStack(stackContents, strCallStack); - - // Convert the timestamp from a QPC value to a trace-relative timestamp. - double millisecondsSinceTraceStart = 0.0; - if(timeStamp.QuadPart != m_fileOpenTimeStamp.QuadPart) - { - LARGE_INTEGER elapsedNanoseconds; - elapsedNanoseconds.QuadPart = timeStamp.QuadPart - m_fileOpenTimeStamp.QuadPart; - millisecondsSinceTraceStart = elapsedNanoseconds.QuadPart / 1000000.0; - } - - StackScratchBuffer scratch; - SString threadFrame; - threadFrame.Printf("Thread (%d)", threadID); - SString event; - event.Printf("{\"Time\" : \"%f\", \"Metric\" : \"1\",\n\"Stack\": [\n\"%s\",\n%s\"%s\"]},", millisecondsSinceTraceStart, message.GetANSI(scratch), strCallStack.GetANSI(scratch), threadFrame.GetANSI(scratch)); - Write(event); -} - -void EventPipeJsonFile::Write(SString &str) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - StackScratchBuffer scratch; - const char * charStr = str.GetANSI(scratch); - - EX_TRY - { - ULONG inCount = str.GetCount(); - ULONG outCount; - - m_pFileStream->Write(charStr, inCount, &outCount); - - if(inCount != outCount) - { - m_writeErrorEncountered = true; - } - } - EX_CATCH - { - m_writeErrorEncountered = true; - } - EX_END_CATCH(SwallowAllExceptions); -} - -void EventPipeJsonFile::FormatCallStack(StackContents &stackContents, SString &resultStr) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - StackScratchBuffer scratch; - SString frameStr; - - for(unsigned int i=0; iGetLoaderModule()->GetAssembly()->GetSimpleName()); - SString fullName; - TypeString::AppendMethodInternal( - fullName, - pMethod, - TypeString::FormatNamespace | TypeString::FormatSignature); - - frameStr.Printf("\"%s!%s\",\n", mAssemblyName.GetANSI(scratch), fullName.GetANSI(scratch)); - resultStr.Append(frameStr); - } -} - -#endif // _DEBUG -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipejsonfile.h b/src/coreclr/vm/eventpipejsonfile.h deleted file mode 100644 index cb420c03360c6..0000000000000 --- a/src/coreclr/vm/eventpipejsonfile.h +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -#ifndef __EVENTPIPE_JSONFILE_H__ -#define __EVENTPIPE_JSONFILE_H__ - -#ifdef _DEBUG -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include "eventpipe.h" -#include "eventpipeeventinstance.h" -#include "fstream.h" - -class EventPipeJsonFile -{ - public: - EventPipeJsonFile(SString &outFilePath); - ~EventPipeJsonFile(); - - // Write an event instance. - void WriteEvent(EventPipeEventInstance &instance); - - // Write an event with the specified message and stack. - void WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents); - - private: - - // Write a string to the file. - void Write(SString &str); - - // Format the input callstack for printing. - void FormatCallStack(StackContents &stackContents, SString &resultStr); - - // The output file stream. - CFileStream *m_pFileStream; - - // Keep track of if an error has been encountered while writing. - bool m_writeErrorEncountered; - - // File-open timestamp for use when calculating event timestamps. - LARGE_INTEGER m_fileOpenTimeStamp; -}; - -#endif // FEATURE_PERFTRACING -#endif // _DEBUG - -#endif // __EVENTPIPE_JSONFILE_H__ diff --git a/src/coreclr/vm/eventpipemetadatagenerator.cpp b/src/coreclr/vm/eventpipemetadatagenerator.cpp deleted file mode 100644 index 6ddbe989fc662..0000000000000 --- a/src/coreclr/vm/eventpipemetadatagenerator.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipemetadatagenerator.h" -#include "eventpipe.h" - -#ifdef FEATURE_PERFTRACING - -bool EventPipeMetadataGenerator::HasV2ParamTypes( - EventPipeParameterDesc *pParams, - UINT32 paramCount) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(paramCount == 0 || pParams != NULL); - } - CONTRACTL_END; - - for (UINT32 i = 0; i < paramCount; ++i) - { - if (pParams[i].Type == EventPipeParameterType::Array) - { - return true; - } - } - - return false; -} - -void EventPipeMetadataGenerator::GetEventMetadataLength( - UINT32 eventID, - LPCWSTR pEventName, - INT64 keywords, - UINT32 version, - EventPipeEventLevel level, - UINT8 opcode, - EventPipeParameterDesc *pParams, - UINT32 paramCount, - size_t *totalLength, - size_t *v2Length) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pEventName != NULL); - PRECONDITION(paramCount == 0 || pParams != NULL); - } - CONTRACTL_END; - - bool hasV2Types = HasV2ParamTypes(pParams, paramCount); - *v2Length = 0; - - // eventID : 4 bytes - // eventName : (eventName.Length + 1) * 2 bytes - // keywords : 8 bytes - // eventVersion : 4 bytes - // level : 4 bytes - // parameterCount : 4 bytes - size_t eventNameLength = wcslen(pEventName); - *totalLength = 24 + ((eventNameLength + 1) * sizeof(WCHAR)); - - if (opcode != 0) - { - // Size of the opcode tag - *totalLength += 6; - } - - if (hasV2Types) - { - // need 4 bytes for the length of the tag - // 1 byte for the tag identifier - // and 4 bytes for the count of params - *totalLength += 9; - // The metadata tag length does not include the required - // length and tag fields - *v2Length = 4; - - // Each parameter has an optional array identifier and then a 4 byte - // TypeCode + the field name (parameterName.Length + 1) * 2 bytes. - for(UINT32 i = 0; i < paramCount; ++i) - { - _ASSERTE(pParams[i].Name != NULL); - // For v2 metadata, fields start with a length (4 bytes) and then the field name - size_t paramSize = 4 + ((wcslen(pParams[i].Name) + 1) * sizeof(WCHAR)); - - if (pParams[i].Type == EventPipeParameterType::Array) - { - // If it's an array type we write the array descriptor (4 bytes) - paramSize += 4; - } - - // Then the typecode - paramSize += 4; - - *totalLength += paramSize; - *v2Length += paramSize; - } - } - else - { - // Each parameter has an 4 byte TypeCode + the field name (parameterName.Length + 1) * 2 bytes - for(UINT32 i = 0; i < paramCount; i++) - { - _ASSERTE(pParams[i].Name != NULL); - *totalLength += (4 + ((wcslen(pParams[i].Name) + 1) * sizeof(WCHAR))); - } - } - -} - -void EventPipeMetadataGenerator::WriteToBuffer(BYTE *pBuffer, size_t bufferLength, size_t *pOffset, LPCWSTR str, size_t strLen) -{ - _ASSERTE(bufferLength >= (*pOffset + strLen + 1)); - - wcsncpy((WCHAR *)(pBuffer + *pOffset), str, strLen); - *pOffset += (strLen * sizeof(WCHAR)); - - // Null terminate the string - *((WCHAR *)(pBuffer + *pOffset)) = W('\0'); - *pOffset += sizeof(WCHAR); -} - -BYTE* EventPipeMetadataGenerator::GenerateEventMetadata( - UINT32 eventID, - LPCWSTR pEventName, - INT64 keywords, - UINT32 version, - EventPipeEventLevel level, - UINT8 opcode, - EventPipeParameterDesc *pParams, - UINT32 paramCount, - size_t *pMetadataLength) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pEventName != NULL); - PRECONDITION(paramCount == 0 || pParams != NULL); - PRECONDITION(pMetadataLength != NULL); - } - CONTRACTL_END; - - size_t totalMetadataLength = 0; - size_t v2MetadataLength = 0; - GetEventMetadataLength(eventID, - pEventName, - keywords, - version, - level, - opcode, - pParams, - paramCount, - &totalMetadataLength, - &v2MetadataLength); - - bool hasV2Types = v2MetadataLength > 0; - *pMetadataLength = totalMetadataLength; - - // Allocate a metadata blob. - BYTE *pMetadata = new BYTE[*pMetadataLength]; - size_t offset = 0; - - // Write the event ID. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, eventID); - - // Write the event name. - size_t eventNameLength = wcslen(pEventName); - WriteToBuffer(pMetadata, *pMetadataLength, &offset, pEventName, eventNameLength); - - // Write the keywords. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, keywords); - - // Write the version. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, version); - - // Write the level. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)level); - - if (hasV2Types) - { - // If we have V2 metadata types, we need to have 0 params for V1 - WriteToBuffer(pMetadata, *pMetadataLength, &offset, 0); - } - else - { - _ASSERTE(!hasV2Types); - - // Write the parameter count. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, paramCount); - // Now write the descriptors - for(UINT32 i = 0; i < paramCount; ++i) - { - EventPipeParameterDesc *pParam = &pParams[i]; - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)pParam->Type); - - size_t parameterNameLength = wcslen(pParam->Name); - WriteToBuffer(pMetadata, *pMetadataLength, &offset, pParam->Name, parameterNameLength); - } - } - - // Now we write optional V2 metadata, if there is any - - if (opcode != 0) - { - // Size of opcode - WriteToBuffer(pMetadata, *pMetadataLength, &offset, 1); - // opcode tag - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (BYTE)EventPipeMetadataTag::Opcode); - // opcode value - WriteToBuffer(pMetadata, *pMetadataLength, &offset, opcode); - } - - if (hasV2Types) - { - // size of V2 metadata payload - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)v2MetadataLength); - // v2 param tag - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (BYTE)EventPipeMetadataTag::ParameterPayload); - // Write the parameter count. - WriteToBuffer(pMetadata, *pMetadataLength, &offset, paramCount); - // Write the parameter descriptions. - for(UINT32 i = 0; i < paramCount; ++i) - { - EventPipeParameterDesc *pParam = &pParams[i]; - size_t parameterNameLength = wcslen(pParam->Name); - size_t parameterNameBytes = ((parameterNameLength + 1) * sizeof(WCHAR)); - if (pParam->Type == EventPipeParameterType::Array) - { - // For an array type, length is 12 (4 bytes length field, 4 bytes array descriptor, 4 bytes typecode) - // + name length - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)(12 + parameterNameBytes)); - // Now write the event name - WriteToBuffer(pMetadata, *pMetadataLength, &offset, pParam->Name, parameterNameLength); - // And there is the array descriptor - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)EventPipeParameterType::Array); - // Now write the underlying type - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)pParam->ElementType); - } - else - { - // For a non array type, length is 8 (4 bytes length field, 4 bytes typecode) - // + name length - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)(8 + parameterNameBytes)); - // Now write the event name - WriteToBuffer(pMetadata, *pMetadataLength, &offset, pParam->Name, parameterNameLength); - // And then the type - WriteToBuffer(pMetadata, *pMetadataLength, &offset, (UINT32)pParam->Type); - } - } - } - - _ASSERTE(*pMetadataLength == offset); - - return pMetadata; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipemetadatagenerator.h b/src/coreclr/vm/eventpipemetadatagenerator.h deleted file mode 100644 index 1f4b5c5b009f6..0000000000000 --- a/src/coreclr/vm/eventpipemetadatagenerator.h +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_METADATAGENERATOR_H__ -#define __EVENTPIPE_METADATAGENERATOR_H__ - -#ifdef FEATURE_PERFTRACING - -enum class EventPipeEventLevel; - -// Represents the type of an event parameter. -// This enum is derived from the managed TypeCode type, though -// not all of these values are available in TypeCode. -// For example, Guid does not exist in TypeCode. -// Keep this in sync with COR_PRF_EVENTPIPE_PARAM_TYPE defined in -// corprof.idl -enum class EventPipeParameterType -{ - Empty = 0, // Null reference - Object = 1, // Instance that isn't a value - DBNull = 2, // Database null value - Boolean = 3, // Boolean - Char = 4, // Unicode character - SByte = 5, // Signed 8-bit integer - Byte = 6, // Unsigned 8-bit integer - Int16 = 7, // Signed 16-bit integer - UInt16 = 8, // Unsigned 16-bit integer - Int32 = 9, // Signed 32-bit integer - UInt32 = 10, // Unsigned 32-bit integer - Int64 = 11, // Signed 64-bit integer - UInt64 = 12, // Unsigned 64-bit integer - Single = 13, // IEEE 32-bit float - Double = 14, // IEEE 64-bit double - Decimal = 15, // Decimal - DateTime = 16, // DateTime - Guid = 17, // Guid - String = 18, // Unicode character string - Array = 19, // Indicates the type is an arbitrary sized array -}; - -enum class EventPipeMetadataTag -{ - Opcode = 1, - ParameterPayload = 2 -}; - -// Contains the metadata associated with an EventPipe event parameter. -struct EventPipeParameterDesc -{ - EventPipeParameterType Type; - // Only used for array types to indicate what type the array elements are - EventPipeParameterType ElementType; - LPCWSTR Name; -}; - -// Generates metadata for an event emitted by the EventPipe. -class EventPipeMetadataGenerator -{ -private: - // Array is not part of TypeCode, we decided to use 19 to represent it. - // (18 is the last type code value, string) - static const UINT32 EventPipeTypeCodeArray = 19; - - static bool HasV2ParamTypes( - EventPipeParameterDesc *pParams, - UINT32 paramCount); - - static void GetEventMetadataLength( - UINT32 eventID, - LPCWSTR pEventName, - INT64 keywords, - UINT32 version, - EventPipeEventLevel level, - UINT8 opcode, - EventPipeParameterDesc *pParams, - UINT32 paramCount, - size_t *totalLength, - size_t *v2Length); - - static void WriteToBuffer(BYTE *pBuffer, size_t bufferLength, size_t *pOffset, LPCWSTR str, size_t strLen); - - template - static void WriteToBuffer(BYTE *pBuffer, size_t bufferLength, size_t *pOffset, T value) - { - _ASSERTE(bufferLength >= (*pOffset + sizeof(T))); - - *(T*)(pBuffer + *pOffset) = value; - *pOffset += sizeof(T); - } - -public: - static BYTE* GenerateEventMetadata( - UINT32 eventID, - LPCWSTR pEventName, - INT64 keywords, - UINT32 version, - EventPipeEventLevel level, - UINT8 opcode, - EventPipeParameterDesc *pParams, - UINT32 paramCount, - size_t *pMetadataLength); -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_METADATAGENERATOR_H__ diff --git a/src/coreclr/vm/eventpipeprotocolhelper.cpp b/src/coreclr/vm/eventpipeprotocolhelper.cpp deleted file mode 100644 index 07ccf9c8b0b73..0000000000000 --- a/src/coreclr/vm/eventpipeprotocolhelper.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "fastserializer.h" -#include "eventpipefile.h" -#include "eventpipeprotocolhelper.h" -#include "eventpipesession.h" -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -#ifdef FEATURE_PERFTRACING - -static bool IsNullOrWhiteSpace(LPCWSTR value) -{ - if (value == nullptr) - return true; - - while (*value) - { - if (!iswspace(*value)) - return false; - ++value; - } - return true; -} - -static bool TryParseCircularBufferSize(uint8_t*& bufferCursor, uint32_t& bufferLen, uint32_t& circularBufferSizeInMB) -{ - const bool CanParse = TryParse(bufferCursor, bufferLen, circularBufferSizeInMB); - return CanParse && (circularBufferSizeInMB > 0); -} - -static bool TryParseSerializationFormat(uint8_t*& bufferCursor, uint32_t& bufferLen, EventPipeSerializationFormat& serializationFormat) -{ - const bool CanParse = TryParse(bufferCursor, bufferLen, (uint32_t&)serializationFormat); - return CanParse && (0 <= (int)serializationFormat) && ((int)serializationFormat < (int)EventPipeSerializationFormat::Count); -} - -static bool TryParseRundownRequested(uint8_t*& bufferCursor, uint32_t& bufferLen, bool& rundownRequested) -{ - return TryParse(bufferCursor, bufferLen, rundownRequested); -} - -const EventPipeCollectTracing2CommandPayload* EventPipeCollectTracing2CommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - } - CONTRACTL_END; - - EventPipeCollectTracing2CommandPayload *payload = new (nothrow) EventPipeCollectTracing2CommandPayload; - if (payload == nullptr) - { - // OOM - return nullptr; - } - - payload->incomingBuffer = lpBuffer; - uint8_t* pBufferCursor = payload->incomingBuffer; - uint32_t bufferLen = BufferSize; - if (!TryParseCircularBufferSize(pBufferCursor, bufferLen, payload->circularBufferSizeInMB) || - !TryParseSerializationFormat(pBufferCursor, bufferLen, payload->serializationFormat) || - !TryParseRundownRequested(pBufferCursor, bufferLen, payload->rundownRequested) || - !EventPipeProtocolHelper::TryParseProviderConfiguration(pBufferCursor, bufferLen, payload->providerConfigs)) - { - delete payload; - return nullptr; - } - - return payload; -} - -const EventPipeCollectTracingCommandPayload* EventPipeCollectTracingCommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - } - CONTRACTL_END; - - EventPipeCollectTracingCommandPayload *payload = new (nothrow) EventPipeCollectTracingCommandPayload; - if (payload == nullptr) - { - // OOM - return nullptr; - } - - payload->incomingBuffer = lpBuffer; - uint8_t* pBufferCursor = payload->incomingBuffer; - uint32_t bufferLen = BufferSize; - if (!TryParseCircularBufferSize(pBufferCursor, bufferLen, payload->circularBufferSizeInMB) || - !TryParseSerializationFormat(pBufferCursor, bufferLen, payload->serializationFormat) || - !EventPipeProtocolHelper::TryParseProviderConfiguration(pBufferCursor, bufferLen, payload->providerConfigs)) - { - delete payload; - return nullptr; - } - - return payload; -} - -void EventPipeProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - switch ((EventPipeCommandId)message.GetHeader().CommandId) - { - case EventPipeCommandId::CollectTracing: - EventPipeProtocolHelper::CollectTracing(message, pStream); - break; - case EventPipeCommandId::CollectTracing2: - EventPipeProtocolHelper::CollectTracing2(message, pStream); - break; - case EventPipeCommandId::StopTracing: - EventPipeProtocolHelper::StopTracing(message, pStream); - break; - - default: - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet); - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND); - delete pStream; - break; - } -} - -bool EventPipeProtocolHelper::TryParseProviderConfiguration(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray &result) -{ - // Picking an arbitrary upper bound, - // This should be larger than any reasonable client request. - const uint32_t MaxCountConfigs = 1000; // TODO: This might be too large. - - uint32_t countConfigs = 0; - if (!TryParse(bufferCursor, bufferLen, countConfigs)) - return false; - if (countConfigs > MaxCountConfigs) - return false; - EventPipeProviderConfiguration *pConfigs = result.AllocNoThrow(countConfigs); - if (pConfigs == nullptr) - return false; - - for (uint32_t i = 0; i < countConfigs; i++) - { - uint64_t keywords = 0; - if (!TryParse(bufferCursor, bufferLen, keywords)) - return false; - - uint32_t logLevel = 0; - if (!TryParse(bufferCursor, bufferLen, logLevel)) - return false; - if (logLevel > 5) // (logLevel > EventPipeEventLevel::Verbose) - return false; - - LPCWSTR pProviderName = nullptr; - if (!TryParseString(bufferCursor, bufferLen, pProviderName)) - return false; - if (IsNullOrWhiteSpace(pProviderName)) - return false; - - LPCWSTR pFilterData = nullptr; // This parameter is optional. - TryParseString(bufferCursor, bufferLen, pFilterData); - - pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData); - } - return (countConfigs > 0); -} - -void EventPipeProtocolHelper::StopTracing(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - NewHolder payload = message.TryParsePayload(); - if (payload == nullptr) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_BAD_ENCODING); - delete pStream; - return; - } - - EventPipe::Disable(payload->sessionId); - - DiagnosticsIpc::IpcMessage stopTracingResponse; - if (stopTracingResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, payload->sessionId)) - stopTracingResponse.Send(pStream); - - bool fSuccess = pStream->Flush(); - if (!fSuccess) - { - // TODO: Add error handling. - } - delete pStream; -} - -void EventPipeProtocolHelper::CollectTracing(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - NewHolder payload = message.TryParsePayload(); - if (payload == nullptr) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_BAD_ENCODING); - delete pStream; - return; - } - - auto sessionId = EventPipe::Enable( - nullptr, // strOutputPath (ignored in this scenario) - payload->circularBufferSizeInMB, // circularBufferSizeInMB - payload->providerConfigs.Ptr(), // pConfigs - static_cast(payload->providerConfigs.Size()), // numConfigs - EventPipeSessionType::IpcStream, // EventPipeSessionType - payload->serializationFormat, // EventPipeSerializationFormat - true, // rundownRequested - pStream); // IpcStream - - if (sessionId == 0) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - delete pStream; - } - else - { - DiagnosticsIpc::IpcMessage successResponse; - if (successResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, sessionId)) - successResponse.Send(pStream); - EventPipe::StartStreaming(sessionId); - } -} - -void EventPipeProtocolHelper::CollectTracing2(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - const EventPipeCollectTracing2CommandPayload* payload = message.TryParsePayload(); - if (payload == nullptr) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_BAD_ENCODING); - delete payload; - delete pStream; - return; - } - - auto sessionId = EventPipe::Enable( - nullptr, // strOutputPath (ignored in this scenario) - payload->circularBufferSizeInMB, // circularBufferSizeInMB - payload->providerConfigs.Ptr(), // pConfigs - static_cast(payload->providerConfigs.Size()), // numConfigs - EventPipeSessionType::IpcStream, // EventPipeSessionType - payload->serializationFormat, // EventPipeSerializationFormat - payload->rundownRequested, // rundownRequested - pStream); // IpcStream - - if (sessionId == 0) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - delete payload; - delete pStream; - } - else - { - DiagnosticsIpc::IpcMessage successResponse; - if (successResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, sessionId)) - successResponse.Send(pStream); - EventPipe::StartStreaming(sessionId); - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeprotocolhelper.h b/src/coreclr/vm/eventpipeprotocolhelper.h deleted file mode 100644 index da6c46f525a01..0000000000000 --- a/src/coreclr/vm/eventpipeprotocolhelper.h +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_PROTOCOL_HELPER_H__ -#define __EVENTPIPE_PROTOCOL_HELPER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include "eventpipe.h" -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -class IpcStream; - -// The event pipe command set is 0x02 -// see diagnosticsipc.h and diagnosticserver.h for more details -enum class EventPipeCommandId : uint8_t -{ - StopTracing = 0x01, - CollectTracing = 0x02, - CollectTracing2 = 0x03, - // future -}; - -// Command = 0x0203 -struct EventPipeCollectTracing2CommandPayload -{ - NewArrayHolder incomingBuffer; - - // The protocol buffer is defined as: - // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z - // message = uint circularBufferMB, uint format, array providers - // uint = 4 little endian bytes - // wchar = 2 little endian bytes, UTF16 encoding - // array = uint length, length # of Ts - // string = (array where the last char must = 0) or (length = 0) - // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data - uint32_t circularBufferSizeInMB; - EventPipeSerializationFormat serializationFormat; - bool rundownRequested; - CQuickArray providerConfigs; - static const EventPipeCollectTracing2CommandPayload* TryParse(BYTE* lpBuffer, uint16_t& BufferSize); -}; - - -// Command = 0x0202 -struct EventPipeCollectTracingCommandPayload -{ - NewArrayHolder incomingBuffer; - - // The protocol buffer is defined as: - // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z - // message = uint circularBufferMB, uint format, array providers - // uint = 4 little endian bytes - // wchar = 2 little endian bytes, UTF16 encoding - // array = uint length, length # of Ts - // string = (array where the last char must = 0) or (length = 0) - // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data - uint32_t circularBufferSizeInMB; - EventPipeSerializationFormat serializationFormat; - CQuickArray providerConfigs; - static const EventPipeCollectTracingCommandPayload* TryParse(BYTE* lpBuffer, uint16_t& BufferSize); -}; - -// Command = 0x0201 -struct EventPipeStopTracingCommandPayload -{ - EventPipeSessionID sessionId; -}; - -class EventPipeProtocolHelper -{ -public: - // IPC event handlers. - static void HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static void StopTracing(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static void CollectTracing(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); // `dotnet-trace collect` - static void CollectTracing2(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static bool TryParseProviderConfiguration(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray &result); - -private: - const static uint32_t IpcStreamReadBufferSize = 8192; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_PROTOCOL_HELPER_H__ diff --git a/src/coreclr/vm/eventpipeprovider.cpp b/src/coreclr/vm/eventpipeprovider.cpp deleted file mode 100644 index 916d7c55a1fa4..0000000000000 --- a/src/coreclr/vm/eventpipeprovider.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipeconfiguration.h" -#include "eventpipeevent.h" -#include "eventpipeprovider.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeProvider::EventPipeProvider( - EventPipeConfiguration *pConfig, - const SString &providerName, - EventPipeCallback pCallbackFunction, - void *pCallbackData) : m_providerName(providerName), - m_sessions(0) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pConfig != NULL); - } - CONTRACTL_END; - - m_deleteDeferred = false; - m_keywords = 0; - m_providerLevel = EventPipeEventLevel::Critical; - m_pEventList = new SList>(); - m_pCallbackFunction = pCallbackFunction; - m_pCallbackData = pCallbackData; - m_pConfig = pConfig; - m_deleteDeferred = false; -} - -EventPipeProvider::~EventPipeProvider() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Free all of the events. - if (m_pEventList != NULL) - { - // We swallow exceptions here because the HOST_BREAKABLE - // lock may throw and this destructor gets called in throw - // intolerant places. If that happens the event list will leak - EX_TRY - { - // Take the lock before manipulating the list. - CrstHolder _crst(EventPipe::GetLock()); - - SListElem *pElem = m_pEventList->GetHead(); - while (pElem != NULL) - { - EventPipeEvent *pEvent = pElem->GetValue(); - delete pEvent; - - SListElem *pCurElem = pElem; - pElem = m_pEventList->GetNext(pElem); - delete pCurElem; - } - - delete m_pEventList; - } - EX_CATCH {} - EX_END_CATCH(SwallowAllExceptions); - - m_pEventList = NULL; - } -} - -const SString &EventPipeProvider::GetProviderName() const -{ - LIMITED_METHOD_CONTRACT; - return m_providerName; -} - -INT64 EventPipeProvider::ComputeEventEnabledMask(INT64 keywords, EventPipeEventLevel eventLevel) const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - return m_pConfig->ComputeEventEnabledMask((*this), keywords, eventLevel); -} - -EventPipeProviderCallbackData EventPipeProvider::SetConfiguration( - INT64 keywordsForAllSessions, - EventPipeEventLevel providerLevelForAllSessions, - uint64_t sessionMask, - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION((m_sessions & sessionMask) == 0); - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - m_sessions |= sessionMask; - - m_keywords = keywordsForAllSessions; - m_providerLevel = providerLevelForAllSessions; - - RefreshAllEvents(); - return PrepareCallbackData(m_keywords, m_providerLevel, pFilterData); -} - -EventPipeProviderCallbackData EventPipeProvider::UnsetConfiguration( - INT64 keywordsForAllSessions, - EventPipeEventLevel providerLevelForAllSessions, - uint64_t sessionMask, - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION((m_sessions & sessionMask) != 0); - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (m_sessions & sessionMask) - m_sessions &= ~sessionMask; - - m_keywords = keywordsForAllSessions; - m_providerLevel = providerLevelForAllSessions; - - RefreshAllEvents(); - return PrepareCallbackData(m_keywords, m_providerLevel, pFilterData); -} - -EventPipeEvent *EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, - BYTE *pMetadata, unsigned int metadataLength) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Create the event. - EventPipeEvent *pEvent = new EventPipeEvent( - *this, - keywords, - eventID, - eventVersion, - level, - needStack, - pMetadata, - metadataLength); - // Add it to the list of events. - AddEvent(*pEvent); - return pEvent; -} - -void EventPipeProvider::AddEvent(EventPipeEvent &event) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Take the config lock before inserting a new event. - CrstHolder _crst(EventPipe::GetLock()); - - m_pEventList->InsertTail(new SListElem(&event)); - event.RefreshState(); -} - -/* static */ void EventPipeProvider::InvokeCallback(EventPipeProviderCallbackData *pEventPipeProviderCallbackData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(!EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - LPCWSTR pFilterData = pEventPipeProviderCallbackData->GetFilterData(); - EventPipeCallback pCallbackFunction = pEventPipeProviderCallbackData->GetCallbackFunction(); - bool enabled = pEventPipeProviderCallbackData->GetEnabled(); - INT64 keywords = pEventPipeProviderCallbackData->GetKeywords(); - EventPipeEventLevel providerLevel = pEventPipeProviderCallbackData->GetProviderLevel(); - void *pCallbackData = pEventPipeProviderCallbackData->GetCallbackData(); - - 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', except when in a quoted string. - SString dstBuffer; - SString(pFilterData).ConvertToUTF8(dstBuffer); - - const COUNT_T BUFFER_SIZE = dstBuffer.GetCount() + 1; - buffer.AllocThrows(BUFFER_SIZE); - BOOL isQuotedValue = false; - COUNT_T j = 0; - for (COUNT_T i = 0; i < BUFFER_SIZE; ++i) - { - // if a value is a quoted string, leave the quotes out from the destination - // and don't replace `=` or `;` characters until leaving the quoted section - // e.g., key="a;value=";foo=bar --> { key\0a;value=\0foo\0bar\0 } - if (dstBuffer[i] == '"') - { - isQuotedValue = !isQuotedValue; - continue; - } - buffer[j++] = ((dstBuffer[i] == '=' || dstBuffer[i] == ';') && !isQuotedValue) ? '\0' : dstBuffer[i]; - } - - // In case we skipped over quotes in the filter string, shrink the buffer size accordingly - if (j < dstBuffer.GetCount()) - buffer.Shrink(j + 1); - - eventFilterDescriptor.Ptr = reinterpret_cast(buffer.Ptr()); - eventFilterDescriptor.Size = static_cast(buffer.Size()); - eventFilterDescriptor.Type = 0; // EventProvider.cs: `internal enum ControllerCommand.Update` - isEventFilterDescriptorInitialized = true; - } - - // NOTE: When we call the callback, we pass in enabled (which is either 1 or 0) as the ControlCode. - // If we want to add new ControlCode, we have to make corresponding change in eventtrace.cpp:EtwCallbackCommon - // to address this. See https://github.com/dotnet/runtime/pull/36733 for more discussions on this. - if (pCallbackFunction != NULL && !g_fEEShutDown) - { - (*pCallbackFunction)( - NULL, /* providerId */ - enabled, /* ControlCode */ - (UCHAR)providerLevel, - keywords, - 0 /* matchAllKeywords */, - isEventFilterDescriptorInitialized ? &eventFilterDescriptor : NULL, - pCallbackData /* CallbackContext */); - } - - buffer.Destroy(); -} - -EventPipeProviderCallbackData EventPipeProvider::PrepareCallbackData( - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - EventPipeProviderCallbackData result(pFilterData, - m_pCallbackFunction, - (m_sessions != 0), - keywords, - providerLevel, - m_pCallbackData, - this); - return result; -} - -bool EventPipeProvider::GetDeleteDeferred() const -{ - LIMITED_METHOD_CONTRACT; - return m_deleteDeferred; -} - -void EventPipeProvider::SetDeleteDeferred() -{ - LIMITED_METHOD_CONTRACT; - m_deleteDeferred = true; - // EventSources will be collected once they ungregister themselves, - // so we can't call back in to them. - m_pCallbackFunction = NULL; -} - -void EventPipeProvider::RefreshAllEvents() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - SListElem *pElem = m_pEventList->GetHead(); - while (pElem != NULL) - { - EventPipeEvent *pEvent = pElem->GetValue(); - pEvent->RefreshState(); - pElem = m_pEventList->GetNext(pElem); - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeprovider.h b/src/coreclr/vm/eventpipeprovider.h deleted file mode 100644 index 864c53d00b282..0000000000000 --- a/src/coreclr/vm/eventpipeprovider.h +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_PROVIDER_H__ -#define __EVENTPIPE_PROVIDER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipeconfiguration.h" -#include "slist.h" - -class EventPipeEvent; - -class EventPipeProvider -{ - // Declare friends. - friend class EventPipe; - friend class EventPipeConfiguration; - -private: - // The name of the provider. - const SString m_providerName; - - // Bit vector containing the currently enabled keywords. - INT64 m_keywords; - - // The current verbosity of the provider. - EventPipeEventLevel m_providerLevel; - - // List of every event currently associated with the provider. - // New events can be added on-the-fly. - SList> *m_pEventList; - - // The optional provider callback. - EventPipeCallback m_pCallbackFunction; - - // The optional provider callback data pointer. - void *m_pCallbackData; - - // The configuration object. - EventPipeConfiguration *m_pConfig; - - // True if the provider has been deleted, but that deletion - // has been deferred until tracing is stopped. - bool m_deleteDeferred; - - // Bit mask of sessions for which this provider is enabled. - uint64_t m_sessions; - - // Private constructor because all providers are created through EventPipe::CreateProvider. - EventPipeProvider(EventPipeConfiguration *pConfig, const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL); - -public: - - ~EventPipeProvider(); - - // Get the provider Name. - const SString& GetProviderName() const; - - // Determine if the provider is enabled. - bool Enabled() const - { - LIMITED_METHOD_CONTRACT; - return (m_sessions != 0); - } - - bool IsEnabled(uint64_t sessionMask) const - { - LIMITED_METHOD_CONTRACT; - return ((m_sessions & sessionMask) != 0); - } - - // Compute the enabled bit mask, the ith bit is 1 iff an event with the given (provider, keywords, eventLevel) is enabled for the ith session. - INT64 ComputeEventEnabledMask(INT64 keywords, EventPipeEventLevel eventLevel) const; - - // Create a new event. - EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, - BYTE *pMetadata = NULL, unsigned int metadataLength = 0); - -private: - - // Add an event to the provider. - void AddEvent(EventPipeEvent &event); - - // Set the provider configuration (enable sets of events). - // This is called by EventPipeConfiguration. - EventPipeProviderCallbackData SetConfiguration( - INT64 keywordsForAllSessions, - EventPipeEventLevel providerLevelForAllSessions, - uint64_t sessionMask, - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData); - - // Unset the provider configuration for the specified session (disable sets of events). - // This is called by EventPipeConfiguration. - EventPipeProviderCallbackData UnsetConfiguration( - INT64 keywordsForAllSessions, - EventPipeEventLevel providerLevelForAllSessions, - uint64_t sessionMask, - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData); - - // Refresh the runtime state of all events. - void RefreshAllEvents(); - - // Prepare the data required for invoking callback - EventPipeProviderCallbackData PrepareCallbackData( - INT64 keywords, - EventPipeEventLevel providerLevel, - LPCWSTR pFilterData); - - // Invoke the provider callback. - static void InvokeCallback(EventPipeProviderCallbackData *pEventPipeProviderCallbackData); - - // Specifies whether or not the provider was deleted, but that deletion - // was deferred until after tracing is stopped. - bool GetDeleteDeferred() const; - - // Defer deletion of the provider. - void SetDeleteDeferred(); -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_PROVIDER_H__ diff --git a/src/coreclr/vm/eventpipesession.cpp b/src/coreclr/vm/eventpipesession.cpp deleted file mode 100644 index 6e85703598c1f..0000000000000 --- a/src/coreclr/vm/eventpipesession.cpp +++ /dev/null @@ -1,618 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipebuffermanager.h" -#include "eventpipefile.h" -#include "eventpipeprovider.h" -#include "eventpipesession.h" -#include "eventpipesessionprovider.h" -#include "eventpipeeventpayload.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeSession::EventPipeSession( - uint32_t index, - LPCWSTR strOutputPath, - IpcStream *const pStream, - EventPipeSessionType sessionType, - EventPipeSerializationFormat format, - bool rundownSwitch, - uint32_t circularBufferSizeInMB, - const EventPipeProviderConfiguration *pProviders, - uint32_t numProviders, - EventPipeSessionSynchronousCallback callback) : m_index(index), - m_pProviderList(new EventPipeSessionProviderList(pProviders, numProviders)), - m_pBufferManager(nullptr), - m_rundownEnabled(false), - m_SessionType(sessionType), - m_format(format), - m_rundownRequested(rundownSwitch), - m_synchronousCallback(callback) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(index < EventPipe::MaxNumberOfSessions); - PRECONDITION(format < EventPipeSerializationFormat::Count); - PRECONDITION(m_SessionType == EventPipeSessionType::Synchronous || circularBufferSizeInMB > 0); - PRECONDITION(numProviders > 0 && pProviders != nullptr); - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - PRECONDITION((m_synchronousCallback != nullptr) == (m_SessionType == EventPipeSessionType::Synchronous)); - } - CONTRACTL_END; - - size_t sequencePointAllocationBudget = 0; - // Hard coded 10MB for now, we'll probably want to make - // this configurable later. - if (GetSessionType() != EventPipeSessionType::Listener && - GetSerializationFormat() >= EventPipeSerializationFormat::NetTraceV4) - { - sequencePointAllocationBudget = 10 * 1024 * 1024; - } - - if (m_SessionType != EventPipeSessionType::Synchronous) - { - m_pBufferManager = new EventPipeBufferManager(this, static_cast(circularBufferSizeInMB) << 20, sequencePointAllocationBudget); - } - - // Create the event pipe file. - // A NULL output path means that we should not write the results to a file. - // This is used in the EventListener case. - m_pFile = nullptr; - switch (sessionType) - { - case EventPipeSessionType::File: - if (strOutputPath != nullptr) - m_pFile = new EventPipeFile(new FileStreamWriter(SString(strOutputPath)), format); - break; - - case EventPipeSessionType::IpcStream: - m_pFile = new EventPipeFile(new IpcStreamWriter(reinterpret_cast(this), pStream), format); - break; - - default: - m_pFile = nullptr; - break; - } - - GetSystemTimeAsFileTime(&m_sessionStartTime); - QueryPerformanceCounter(&m_sessionStartTimeStamp); - this->m_paused = false; -} - -EventPipeSession::~EventPipeSession() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(!m_ipcStreamingEnabled); - } - CONTRACTL_END; - - delete m_pProviderList; - if (m_pBufferManager != nullptr) - { - delete m_pBufferManager; - } - delete m_pFile; -} - -bool EventPipeSession::HasIpcStreamingStarted() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - return m_pIpcStreamingThread != nullptr ? m_pIpcStreamingThread->HasStarted() : false; -} - -void EventPipeSession::SetThreadShutdownEvent() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - // Signal Disable() that the thread has been destroyed. - m_threadShutdownEvent.Set(); -} - -static void PlatformSleep() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - // Wait until it's time to sample again. - const uint32_t PeriodInNanoSeconds = 100000000; // 100 msec. - -#ifdef TARGET_UNIX - PAL_nanosleep(PeriodInNanoSeconds); -#else //TARGET_UNIX - const uint32_t NUM_NANOSECONDS_IN_1_MS = 1000000; - ClrSleepEx(PeriodInNanoSeconds / NUM_NANOSECONDS_IN_1_MS, FALSE); -#endif //TARGET_UNIX -} - -DWORD WINAPI EventPipeSession::ThreadProc(void *args) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(args != nullptr); - } - CONTRACTL_END; - - if (args == nullptr) - return 1; - - EventPipeSession *const pEventPipeSession = reinterpret_cast(args); - if (pEventPipeSession->GetSessionType() != EventPipeSessionType::IpcStream) - return 1; - - if (!pEventPipeSession->HasIpcStreamingStarted()) - return 1; - - Thread *const pThisThread = pEventPipeSession->GetIpcStreamingThread(); - bool fSuccess = true; - CLREvent *waitEvent = pEventPipeSession->GetWaitEvent(); - - { - GCX_PREEMP(); - EX_TRY - { - while (pEventPipeSession->IsIpcStreamingEnabled()) - { - bool eventsWritten = false; - if (!pEventPipeSession->WriteAllBuffersToFile(&eventsWritten)) - { - fSuccess = false; - break; - } - - if (!eventsWritten) - { - // No events were available, sleep until more are available - waitEvent->Wait(INFINITE, FALSE); - } - - // Wait until it's time to sample again. - PlatformSleep(); - } - - pEventPipeSession->SetThreadShutdownEvent(); - } - EX_CATCH - { - pEventPipeSession->SetThreadShutdownEvent(); - // TODO: STRESS_LOG ? - // TODO: Notify `EventPipe` itself to remove this session from the list. - } - EX_END_CATCH(SwallowAllExceptions); - } - - EX_TRY - { - if (!fSuccess) - EventPipe::Disable(reinterpret_cast(pEventPipeSession)); - } - EX_CATCH - { - // TODO: STRESS_LOG ? - } - EX_END_CATCH(SwallowAllExceptions); - - if (pThisThread != nullptr) - ::DestroyThread(pThisThread); - - return 0; -} - -void EventPipeSession::CreateIpcStreamingThread() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(m_SessionType == EventPipeSessionType::IpcStream); - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - m_ipcStreamingEnabled = true; - m_pIpcStreamingThread = SetupUnstartedThread(); - if (m_pIpcStreamingThread->CreateNewThread(0, ThreadProc, this)) - { - m_pIpcStreamingThread->SetBackground(TRUE); - m_pIpcStreamingThread->StartThread(); - } - else - { - _ASSERT(!"Unable to create IPC stream flushing thread."); - } - m_threadShutdownEvent.CreateManualEvent(FALSE); -} - -bool EventPipeSession::IsValid() const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - return !m_pProviderList->IsEmpty(); -} - -void EventPipeSession::AddSessionProvider(EventPipeSessionProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - m_pProviderList->AddSessionProvider(pProvider); -} - -EventPipeSessionProvider *EventPipeSession::GetSessionProvider(const EventPipeProvider *pProvider) const -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - return m_pProviderList->GetSessionProvider(pProvider); -} - -bool EventPipeSession::WriteAllBuffersToFile(bool *pEventsWritten) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if (m_pFile == nullptr || m_pBufferManager == nullptr) - return true; - - // Get the current time stamp. - // EventPipeBufferManager::WriteAllBuffersToFile will use this to ensure that no events after - // the current timestamp are written into the file. - LARGE_INTEGER stopTimeStamp; - QueryPerformanceCounter(&stopTimeStamp); - m_pBufferManager->WriteAllBuffersToFile(m_pFile, stopTimeStamp, pEventsWritten); - return !m_pFile->HasErrors(); -} - -void EventPipeSession::Pause() -{ - this->m_paused = true; -} - -void EventPipeSession::Resume() -{ - this->m_paused = false; -} - -bool EventPipeSession::WriteEvent( - Thread *pThread, - EventPipeEvent &event, - EventPipeEventPayload &payload, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId, - Thread *pEventThread, - StackContents *pStack) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (this->m_paused) - { - return true; - } - - // Filter events specific to "this" session based on precomputed flag on provider/events. - if (event.IsEnabled(GetMask())) - { - if (m_synchronousCallback != nullptr) - { - m_synchronousCallback(event.GetProvider(), - event.GetEventID(), - event.GetEventVersion(), - event.GetMetadataLength(), - event.GetMetadata(), - payload.GetSize(), - payload.GetFlatData(), - pActivityId, - pRelatedActivityId, - pEventThread, - pStack == nullptr ? 0 : pStack->GetSize(), - pStack == nullptr ? nullptr : reinterpret_cast(pStack->GetPointer())); - return true; - } - else - { - _ASSERTE(m_pBufferManager != nullptr); - return m_pBufferManager->WriteEvent(pThread, *this, event, payload, pActivityId, pRelatedActivityId, pEventThread, pStack); - } - } - - return false; -} - -void EventPipeSession::WriteSequencePointUnbuffered() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pFile == nullptr || m_pBufferManager == nullptr) - return; - EventPipeSequencePoint sequencePoint; - m_pBufferManager->InitSequencePointThreadList(&sequencePoint); - m_pFile->WriteSequencePoint(&sequencePoint); -} - -EventPipeEventInstance *EventPipeSession::GetNextEvent() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(!EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (m_pBufferManager == nullptr) - { - // Shouldn't call GetNextEvent on a synchronous session - _ASSERTE(false); - return nullptr; - } - - return m_pBufferManager->GetNextEvent(); -} - -CLREvent *EventPipeSession::GetWaitEvent() -{ - LIMITED_METHOD_CONTRACT; - - if (m_pBufferManager == nullptr) - { - // Shouldn't call GetWaitEvent on a synchronous session - _ASSERTE(false); - return nullptr; - } - - return m_pBufferManager->GetWaitEvent(); -} - -void EventPipeSession::StartStreaming() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - // Lock must be held by EventPipe::Enable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (m_pFile != nullptr) - m_pFile->InitializeFile(); - - if (m_SessionType == EventPipeSessionType::IpcStream) - CreateIpcStreamingThread(); - - if (m_SessionType == EventPipeSessionType::Synchronous) - { - _ASSERTE(m_pFile == nullptr); - _ASSERTE(!IsIpcStreamingEnabled()); - } -} - -void EventPipeSession::EnableRundown() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - // Lock must be held by EventPipe::Enable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - //! The keywords below seems to correspond to: - //! LoaderKeyword (0x00000008) - //! JitKeyword (0x00000010) - //! NgenKeyword (0x00000020) - //! unused_keyword (0x00000100) - //! JittedMethodILToNativeMapKeyword (0x00020000) - //! ThreadTransferKeyword (0x80000000) - const UINT64 Keywords = 0x80020138; - const UINT32 VerboseLoggingLevel = static_cast(EventPipeEventLevel::Verbose); - const EventPipeProviderConfiguration RundownProviders[] = { - {W("Microsoft-Windows-DotNETRuntime"), Keywords, VerboseLoggingLevel, NULL}, // Public provider. - {W("Microsoft-Windows-DotNETRuntimeRundown"), Keywords, VerboseLoggingLevel, NULL} // Rundown provider. - }; - const uint32_t RundownProvidersSize = sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration); - - // update the provider context here since the callback doesn't happen till we actually try to do rundown. - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.Level = VerboseLoggingLevel; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.EnabledKeywordsBitmask = Keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled = true; - - // Update provider list with rundown configuration. - for (uint32_t i = 0; i < RundownProvidersSize; ++i) - { - const EventPipeProviderConfiguration &Config = RundownProviders[i]; - m_pProviderList->AddSessionProvider(new EventPipeSessionProvider( - Config.GetProviderName(), - Config.GetKeywords(), - (EventPipeEventLevel)Config.GetLevel(), - Config.GetFilterData())); - } - - m_rundownEnabled = true; -} - -void EventPipeSession::DisableIpcStreamingThread() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(m_SessionType == EventPipeSessionType::IpcStream); - PRECONDITION(m_ipcStreamingEnabled); - } - CONTRACTL_END; - - _ASSERTE(!g_fProcessDetach); - _ASSERTE(m_pBufferManager != nullptr); - - // The IPC streaming thread will watch this value and exit - // when profiling is disabled. - m_ipcStreamingEnabled = false; - - // Thread could be waiting on the event that there is new data to read. - m_pBufferManager->GetWaitEvent()->Set(); - - // Wait for the sampling thread to clean itself up. - m_threadShutdownEvent.Wait(INFINITE, FALSE /* bAlertable */); - m_threadShutdownEvent.CloseEvent(); -} - -void EventPipeSession::Disable() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if ((m_SessionType == EventPipeSessionType::IpcStream) && m_ipcStreamingEnabled) - DisableIpcStreamingThread(); - - bool ignored; - WriteAllBuffersToFile(&ignored); - m_pProviderList->Clear(); -} - -void EventPipeSession::SuspendWriteEvent() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - // Need to disable the session before calling this method - PRECONDITION(!EventPipe::IsSessionEnabled(reinterpret_cast(this))); - } - CONTRACTL_END; - - // Collect all threads that are currently active so we don't have to - // wait for events to finish writing under the lock. - CQuickArrayList threadList; - { - SpinLockHolder holder(EventPipeThread::GetGlobalThreadLock()); - - EventPipeThreadIterator eventPipeThreads = EventPipeThread::GetThreads(); - EventPipeThread *pThread = nullptr; - while (eventPipeThreads.Next(&pThread)) - { - // Add ref so the thread doesn't disappear when we release the lock - pThread->AddRef(); - threadList.Push(pThread); - } - } - - for (size_t i = 0; i < threadList.Size(); i++) - { - EventPipeThread *pThread = threadList[i]; - // Wait for the thread to finish any writes to this session - YIELD_WHILE(pThread->GetSessionWriteInProgress() == GetIndex()); - - // Since we've already disabled the session, the thread won't call back in to this - // session once its done with the current write - - pThread->Release(); - } - - if (m_pBufferManager != nullptr) - { - // Convert all buffers to read only to ensure they get flushed - m_pBufferManager->SuspendWriteEvent(GetIndex()); - } -} - -void EventPipeSession::ExecuteRundown() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - // Lock must be held by EventPipe::Disable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (m_pFile == nullptr) - return; - - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0) - { - // Ask the runtime to emit rundown events. - if (g_fEEStarted && !g_fEEShutDown) - ETW::EnumerationLog::EndRundown(); - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipesession.h b/src/coreclr/vm/eventpipesession.h deleted file mode 100644 index 39d91f8536071..0000000000000 --- a/src/coreclr/vm/eventpipesession.h +++ /dev/null @@ -1,280 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_SESSION_H__ -#define __EVENTPIPE_SESSION_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include "threadsuspend.h" -#include "eventpipesessionprovider.h" - -class EventPipeBufferManager; -class EventPipeEventInstance; -class EventPipeFile; -class EventPipeThread; - -// TODO: Revisit the need of this enum and its usage. -enum class EventPipeSessionType -{ - File, - Listener, - IpcStream, - Synchronous -}; - -enum class EventPipeSerializationFormat -{ - // Default format used in .Net Core 2.0-3.0 Preview 6 - // TBD - it may remain the default format .Net Core 3.0 when - // used with private EventPipe managed API via reflection. - // This format had limited official exposure in documented - // end-user RTM scenarios, but it is supported by PerfView, - // TraceEvent, and was used by AI profiler - NetPerfV3, - - // Default format we plan to use in .Net Core 3 Preview7+ - // for most if not all scenarios - NetTraceV4, - - Count -}; - -//! Encapsulates an EventPipe session information and memory management. -class EventPipeSession -{ -private: - const uint32_t m_index; - - // The set of configurations for each provider in the session. - EventPipeSessionProviderList *const m_pProviderList; - - // Session buffer manager. - EventPipeBufferManager * m_pBufferManager; - - // True if rundown is enabled. - Volatile m_rundownEnabled; - - // The type of the session. - // This determines behavior within the system (e.g. policies around which events to drop, etc.) - const EventPipeSessionType m_SessionType; - - // For file/IPC sessions this controls the format emitted. For in-proc EventListener it is - // irrelevant. - EventPipeSerializationFormat m_format; - - // For determininig if a particular session needs rundown events - const bool m_rundownRequested; - - // For synchronous sessions - EventPipeSessionSynchronousCallback m_synchronousCallback; - - // Start date and time in UTC. - FILETIME m_sessionStartTime; - - // Start timestamp. - LARGE_INTEGER m_sessionStartTimeStamp; - - // Object used to flush event data (File, IPC stream, etc.). - EventPipeFile *m_pFile; - - // Data members used when an IPC streaming thread is used. - Volatile m_ipcStreamingEnabled = false; - - // When the session is of IPC type, this becomes a handle to the streaming thread. - Thread *m_pIpcStreamingThread = nullptr; - - // Event object used to signal Disable that the IPC streaming thread is done. - CLREvent m_threadShutdownEvent; - - void CreateIpcStreamingThread(); - - static DWORD WINAPI ThreadProc(void *args); - - void SetThreadShutdownEvent(); - - void DisableIpcStreamingThread(); - - // Note - access to this field is NOT synchronized - // - // This field is currently modified in EventPipe::EnableViaEnvironmentVariables() during process startup - // and GCToEEInterface::AnalyzeSurvivorsFinished() while the GC has already synchronized all the threads. - // - // It is read in EventPipeSession::WriteEvent(). While it is possible for other preemptive threads to read - // the field while GC is happening, it should not happen because the only gcGenAwareSession only subscribe - // to GC events. - // - // This functionality is a workaround because we couldn't safely Enable()/Disable() the session where we wanted to due to lock-leveling. - // we expect to remove it in the future once that limitation is resolved - // other scenarios are discouraged from using this given that we plan to make it go away - bool m_paused; - -public: - EventPipeSession( - uint32_t index, - LPCWSTR strOutputPath, - IpcStream *const pStream, - EventPipeSessionType sessionType, - EventPipeSerializationFormat format, - bool rundownRequested, - uint32_t circularBufferSizeInMB, - const EventPipeProviderConfiguration *pProviders, - uint32_t numProviders, - EventPipeSessionSynchronousCallback callback = nullptr); - - ~EventPipeSession(); - - /** - * Please do not use this function, see EventPipeSession::m_paused for more information - */ - void Pause(); - - /** - * Please do not use this function, see EventPipeSession::m_paused for more information - */ - void Resume(); - - /** - * Please do not use this function, see EventPipeSession::m_paused for more information - */ - bool Paused() - { - return this->m_paused; - } - - uint64_t GetMask() const - { - LIMITED_METHOD_CONTRACT; - return ((uint64_t)1 << m_index); - } - - uint32_t GetIndex() const - { - LIMITED_METHOD_CONTRACT; - return m_index; - } - - // Get the session type. - EventPipeSessionType GetSessionType() const - { - LIMITED_METHOD_CONTRACT; - return m_SessionType; - } - - // Get the format version used by the file/IPC serializer - EventPipeSerializationFormat GetSerializationFormat() const - { - LIMITED_METHOD_CONTRACT; - return m_format; - } - - // Get whether rundown was requested by the client. - bool RundownRequested() const - { - LIMITED_METHOD_CONTRACT; - return m_rundownRequested; - } - - // Determine if rundown is enabled. - bool RundownEnabled() const - { - LIMITED_METHOD_CONTRACT; - return m_rundownEnabled; - } - - // Get the session start time in UTC. - FILETIME GetStartTime() const - { - LIMITED_METHOD_CONTRACT; - return m_sessionStartTime; - } - - // Get the session start timestamp. - LARGE_INTEGER GetStartTimeStamp() const - { - LIMITED_METHOD_CONTRACT; - return m_sessionStartTimeStamp; - } - - bool IsIpcStreamingEnabled() const - { - LIMITED_METHOD_CONTRACT; - return m_ipcStreamingEnabled; - } - -#ifdef DEBUG - EventPipeBufferManager* GetBufferManager() const - { - LIMITED_METHOD_CONTRACT; - return m_pBufferManager; - } -#endif - - Thread *GetIpcStreamingThread() const - { - LIMITED_METHOD_CONTRACT; - return m_pIpcStreamingThread; - } - - EventPipeSessionProviderIterator GetProviders() - { - LIMITED_METHOD_CONTRACT; - return m_pProviderList->GetProviders(); - } - - // Add a new provider to the session. - void AddSessionProvider(EventPipeSessionProvider *pProvider); - - // Get the session provider for the specified provider if present. - EventPipeSessionProvider* GetSessionProvider(const EventPipeProvider *pProvider) const; - - bool WriteAllBuffersToFile(bool *pEventsWritten); - - // If a session is non-synchronous (i.e. a file, pipe, etc) WriteEvent will - // put the event in a buffer and return as quick as possible. If a session is - // synchronous (callback to the profiler) then this method will block until the - // profiler is done parsing and reacting to it. - bool WriteEvent( - Thread *pThread, - EventPipeEvent &event, - EventPipeEventPayload &payload, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId, - Thread *pEventThread = nullptr, - StackContents *pStack = nullptr); - - // Write a sequence point into the output stream synchronously - void WriteSequencePointUnbuffered(); - - EventPipeEventInstance *GetNextEvent(); - - CLREvent *GetWaitEvent(); - - // Enable a session in the event pipe. - // MUST be called AFTER sending the IPC response - // Side effects: - // - sends file header information for nettrace format - // - turns on IpcStreaming thread which flushes events to stream - void StartStreaming(); - - // Disable a session in the event pipe. - // side-effects: writes all buffers to stream/file - void Disable(); - - // Force all in-progress writes to either finish or cancel - // This is required to ensure we can safely flush and delete the buffers - void SuspendWriteEvent(); - - void EnableRundown(); - void ExecuteRundown(); - - // Determine if the session is valid or not. Invalid sessions can be detected before they are enabled. - bool IsValid() const; - - bool HasIpcStreamingStarted() /* This is not const because CrtsHolder does not take a const* */; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_SESSION_H__ diff --git a/src/coreclr/vm/eventpipesessionprovider.cpp b/src/coreclr/vm/eventpipesessionprovider.cpp deleted file mode 100644 index 939aab0f60350..0000000000000 --- a/src/coreclr/vm/eventpipesessionprovider.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipeprovider.h" -#include "eventpipesessionprovider.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeSessionProvider::EventPipeSessionProvider( - LPCWSTR providerName, - UINT64 keywords, - EventPipeEventLevel loggingLevel, - LPCWSTR filterData) -{ - 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; - - 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() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - delete[] m_pProviderName; - delete[] m_pFilterData; -} - -EventPipeSessionProviderList::EventPipeSessionProviderList( - const EventPipeProviderConfiguration *pConfigs, - uint32_t numConfigs) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION((numConfigs == 0) || (numConfigs > 0 && pConfigs != nullptr)); - } - CONTRACTL_END; - - m_pProviders = new SList>(); - m_pCatchAllProvider = NULL; - - if ((numConfigs > 0) && (pConfigs == nullptr)) - return; - - for (uint32_t i = 0; i < numConfigs; ++i) - { - const EventPipeProviderConfiguration *pConfig = &pConfigs[i]; - - // 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, - NULL); - } - else - { - EventPipeSessionProvider *pProvider = new EventPipeSessionProvider( - pConfig->GetProviderName(), - pConfig->GetKeywords(), - (EventPipeEventLevel)pConfig->GetLevel(), - pConfig->GetFilterData()); - - m_pProviders->InsertTail(new SListElem(pProvider)); - } - } -} - -EventPipeSessionProviderList::~EventPipeSessionProviderList() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - Clear(); - delete m_pProviders; - delete m_pCatchAllProvider; -} - -void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pProvider != nullptr); - } - CONTRACTL_END; - - if (pProvider != nullptr) - m_pProviders->InsertTail(new SListElem(pProvider)); -} - -EventPipeSessionProvider *EventPipeSessionProviderList::GetSessionProvider(const EventPipeProvider *pProvider) const -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pProvider != nullptr); - } - CONTRACTL_END; - - if (pProvider == nullptr) - return nullptr; - - // 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 *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; -} - -EventPipeSessionProviderIterator EventPipeSessionProviderList::GetProviders() -{ - LIMITED_METHOD_CONTRACT; - return EventPipeSessionProviderIterator(m_pProviders); -} - -bool EventPipeSessionProviderList::IsEmpty() const -{ - LIMITED_METHOD_CONTRACT; - - return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL); -} - -void EventPipeSessionProviderList::Clear() -{ - if (m_pProviders != NULL) - { - while (!m_pProviders->IsEmpty()) - { - SListElem *pElem = m_pProviders->RemoveHead(); - EventPipeSessionProvider *pProvider = pElem->GetValue(); - delete pProvider; - delete pElem; - } - } - - _ASSERTE(m_pProviders->IsEmpty()); -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipesessionprovider.h b/src/coreclr/vm/eventpipesessionprovider.h deleted file mode 100644 index 37c715bdf36b3..0000000000000 --- a/src/coreclr/vm/eventpipesessionprovider.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __EVENTPIPE_SESSION_PROVIDER_SESSION_H__ -#define __EVENTPIPE_SESSION_PROVIDER_SESSION_H__ - -#ifdef FEATURE_PERFTRACING - -enum class EventPipeEventLevel; -class EventPipeProvider; - -typedef EventPipeIterator EventPipeSessionProviderIterator; - -class EventPipeSessionProvider -{ -public: - EventPipeSessionProvider( - LPCWSTR providerName, - UINT64 keywords, - EventPipeEventLevel loggingLevel, - LPCWSTR filterData); - ~EventPipeSessionProvider(); - - LPCWSTR GetProviderName() const - { - return m_pProviderName; - } - - UINT64 GetKeywords() const - { - return m_keywords; - } - - EventPipeEventLevel GetLevel() const - { - return m_loggingLevel; - } - - LPCWSTR GetFilterData() const - { - return m_pFilterData; - } - -private: - WCHAR *m_pProviderName; - UINT64 m_keywords; - EventPipeEventLevel m_loggingLevel; - WCHAR *m_pFilterData; -}; - -class EventPipeSessionProviderList -{ -public: - // Create a new list based on the input. - EventPipeSessionProviderList( - const EventPipeProviderConfiguration *pConfigs, - uint32_t numConfigs); - ~EventPipeSessionProviderList(); - - // 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(const EventPipeProvider *pProvider) const; - - EventPipeSessionProviderIterator GetProviders(); - - // Returns true if the list is empty. - bool IsEmpty() const; - - // Clear the list of providers. - void Clear(); - - EventPipeSessionProviderList() = delete; - EventPipeSessionProviderList(const EventPipeSessionProviderList &other) = delete; - EventPipeSessionProviderList(EventPipeSessionProviderList &&other) = delete; - EventPipeSessionProviderList &operator=(const EventPipeSessionProviderList &rhs) = delete; - EventPipeSessionProviderList &&operator=(EventPipeSessionProviderList &&rhs) = delete; - -private: - SList> *m_pProviders = nullptr; - - // A catch-all provider used when tracing is enabled for all events. - EventPipeSessionProvider *m_pCatchAllProvider = nullptr; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_SESSION_PROVIDER_SESSION_H__ diff --git a/src/coreclr/vm/eventpipethread.cpp b/src/coreclr/vm/eventpipethread.cpp deleted file mode 100644 index f33fc4ffd840f..0000000000000 --- a/src/coreclr/vm/eventpipethread.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipe.h" -#include "eventpipebuffer.h" -#include "eventpipebuffermanager.h" - -#ifdef FEATURE_PERFTRACING - -EventPipeThreadSessionState::EventPipeThreadSessionState(EventPipeThread* pThread, EventPipeSession* pSession DEBUG_ARG(EventPipeBufferManager* pBufferManager)) : - m_pThread(pThread), - m_pSession(pSession), - m_pWriteBuffer(nullptr), - m_pBufferList(nullptr), -#ifdef DEBUG - m_pBufferManager(pBufferManager), -#endif - m_sequenceNumber(1) -{ -} - -EventPipeThread* EventPipeThreadSessionState::GetThread() -{ - LIMITED_METHOD_CONTRACT; - return m_pThread; -} - -EventPipeSession* EventPipeThreadSessionState::GetSession() -{ - LIMITED_METHOD_CONTRACT; - return m_pSession; -} - -EventPipeBuffer *EventPipeThreadSessionState::GetWriteBuffer() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pThread->IsLockOwnedByCurrentThread()); - - _ASSERTE((m_pWriteBuffer == nullptr) || (m_pWriteBuffer->GetVolatileState() == EventPipeBufferState::WRITABLE)); - return m_pWriteBuffer; -} - -void EventPipeThreadSessionState::SetWriteBuffer(EventPipeBuffer *pNewBuffer) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pThread->IsLockOwnedByCurrentThread()); - _ASSERTE((pNewBuffer == nullptr) || pNewBuffer->GetVolatileState() == EventPipeBufferState::WRITABLE); - - _ASSERTE((m_pWriteBuffer == nullptr) || (m_pWriteBuffer->GetVolatileState() == EventPipeBufferState::WRITABLE)); - if (m_pWriteBuffer != nullptr) - m_pWriteBuffer->ConvertToReadOnly(); - m_pWriteBuffer = pNewBuffer; -} - -EventPipeBufferList *EventPipeThreadSessionState::GetBufferList() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pBufferManager->IsLockOwnedByCurrentThread()); - return m_pBufferList; -} - -void EventPipeThreadSessionState::SetBufferList(EventPipeBufferList *pNewBufferList) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pBufferManager->IsLockOwnedByCurrentThread()); - m_pBufferList = pNewBufferList; -} - -unsigned int EventPipeThreadSessionState::GetVolatileSequenceNumber() -{ - LIMITED_METHOD_CONTRACT; - return m_sequenceNumber.LoadWithoutBarrier(); -} - -unsigned int EventPipeThreadSessionState::GetSequenceNumber() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pThread->IsLockOwnedByCurrentThread()); - return m_sequenceNumber.LoadWithoutBarrier(); -} - -void EventPipeThreadSessionState::IncrementSequenceNumber() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_pThread->IsLockOwnedByCurrentThread()); - m_sequenceNumber++; -} - -void ReleaseEventPipeThreadRef(EventPipeThread *pThread) -{ - LIMITED_METHOD_CONTRACT; - pThread->Release(); -} - -void AcquireEventPipeThreadRef(EventPipeThread *pThread) -{ - LIMITED_METHOD_CONTRACT; - pThread->AddRef(); -} - -thread_local EventPipeThreadHolder EventPipeThread::gCurrentEventPipeThreadHolder; - -SpinLock EventPipeThread::s_threadsLock; -SList> EventPipeThread::s_pThreads; - -EventPipeThread::EventPipeThread() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_lock.Init(LOCK_TYPE_DEFAULT); - m_refCount = 0; - -#ifdef TARGET_UNIX - m_osThreadId = ::PAL_GetCurrentOSThreadId(); -#else - m_osThreadId = ::GetCurrentThreadId(); -#endif - memset(m_sessionState, 0, sizeof(EventPipeThreadSessionState*) * EventPipe::MaxNumberOfSessions); -} - -EventPipeThread::~EventPipeThread() -{ - LIMITED_METHOD_CONTRACT; -#ifdef DEBUG - for (uint32_t i = 0; i < EventPipe::MaxNumberOfSessions; i++) - { - _ASSERTE(m_sessionState[i] == NULL); - } -#endif -} - -void EventPipeThread::Initialize() -{ - LIMITED_METHOD_CONTRACT; - - s_threadsLock.Init(LOCK_TYPE_DEFAULT); -} - -EventPipeThread *EventPipeThread::Get() -{ - LIMITED_METHOD_CONTRACT; - return gCurrentEventPipeThreadHolder; -} - -EventPipeThread* EventPipeThread::GetOrCreate() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (gCurrentEventPipeThreadHolder == nullptr) - { - EX_TRY - { - gCurrentEventPipeThreadHolder = new EventPipeThread(); - - { - SpinLockHolder crst(&s_threadsLock); - s_pThreads.InsertTail(new SListElem((EventPipeThread *)gCurrentEventPipeThreadHolder)); - } - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions); - } - return gCurrentEventPipeThreadHolder; -} - -EventPipeThreadIterator EventPipeThread::GetThreads() -{ - LIMITED_METHOD_CONTRACT; - - return EventPipeThreadIterator(&s_pThreads); -} - -void EventPipeThread::AddRef() -{ - LIMITED_METHOD_CONTRACT; - FastInterlockIncrement(&m_refCount); -} - -void EventPipeThread::Release() -{ - LIMITED_METHOD_CONTRACT; - - if (FastInterlockDecrement(&m_refCount) == 0) - { - SpinLockHolder crst(&s_threadsLock); - - // Remove ourselves from the global list - SListElem *pElem = s_pThreads.GetHead(); - while (pElem != nullptr) - { - // A null value shouldn't ever be added to the list - _ASSERTE(pElem->GetValue() != nullptr); - - if (pElem->GetValue() == this) - { - break; - } - - pElem = s_pThreads.GetNext(pElem); - } - - if (pElem == nullptr || s_pThreads.FindAndRemove(pElem) == nullptr) - { - // We should always be in the list - _ASSERTE(!"We couldn't find ourselves in the global thread list"); - } - - // https://isocpp.org/wiki/faq/freestore-mgmt#delete-this - // As long as you're careful, it's okay (not evil) for an object to commit suicide (delete this). - delete this; - } -} - -EventPipeThreadSessionState *EventPipeThread::GetOrCreateSessionState(EventPipeSession *pSession) -{ - LIMITED_METHOD_CONTRACT; - PRECONDITION(pSession != nullptr); - PRECONDITION(pSession->GetIndex() < EventPipe::MaxNumberOfSessions); - PRECONDITION(IsLockOwnedByCurrentThread()); - - EventPipeThreadSessionState *pState = m_sessionState[pSession->GetIndex()]; - if (pState == nullptr) - { - pState = new (nothrow) EventPipeThreadSessionState(this, pSession DEBUG_ARG(pSession->GetBufferManager())); - m_sessionState[pSession->GetIndex()] = pState; - } - return pState; -} - -EventPipeThreadSessionState *EventPipeThread::GetSessionState(EventPipeSession *pSession) -{ - LIMITED_METHOD_CONTRACT; - PRECONDITION(pSession != nullptr); - PRECONDITION(pSession->GetIndex() < EventPipe::MaxNumberOfSessions); - PRECONDITION(IsLockOwnedByCurrentThread()); - - EventPipeThreadSessionState *const pState = m_sessionState[pSession->GetIndex()]; - _ASSERTE(pState != nullptr); - return pState; -} - -void EventPipeThread::DeleteSessionState(EventPipeSession* pSession) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(pSession != nullptr); - _ASSERTE(IsLockOwnedByCurrentThread()); - - unsigned int index = pSession->GetIndex(); - _ASSERTE(index < EventPipe::MaxNumberOfSessions); - EventPipeThreadSessionState* pState = m_sessionState[index]; - - _ASSERTE(pState != nullptr); - delete pState; - m_sessionState[index] = nullptr; -} - -SpinLock* EventPipeThread::GetLock() -{ - LIMITED_METHOD_CONTRACT; - return &m_lock; -} - -#ifdef DEBUG -bool EventPipeThread::IsLockOwnedByCurrentThread() -{ - LIMITED_METHOD_CONTRACT; - return m_lock.OwnedByCurrentThread(); -} -#endif - -SIZE_T EventPipeThread::GetOSThreadId() -{ - LIMITED_METHOD_CONTRACT; - return m_osThreadId; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipethread.h b/src/coreclr/vm/eventpipethread.h deleted file mode 100644 index f4d899a9d697b..0000000000000 --- a/src/coreclr/vm/eventpipethread.h +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __EVENTPIPE_THREAD_H__ -#define __EVENTPIPE_THREAD_H__ - -#ifdef FEATURE_PERFTRACING - -#include "eventpipe.h" -#include "eventpipebuffer.h" -#include "eventpipesession.h" -#include "spinlock.h" - -class EventPipeBuffer; -class EventPipeBufferList; -class EventPipeBufferManager; -class EventPipeThread; - -void ReleaseEventPipeThreadRef(EventPipeThread* pThread); -void AcquireEventPipeThreadRef(EventPipeThread* pThread); -typedef Wrapper EventPipeThreadHolder; - -typedef EventPipeIterator EventPipeThreadIterator; - -class EventPipeThreadSessionState -{ - // immutable - EventPipeThreadHolder m_pThread; - - // immutable - EventPipeSession* m_pSession; - - // The buffer this thread is allowed to write to if non-null, it must - // match the tail of m_bufferList - // protected by m_pThread::GetLock() - EventPipeBuffer* m_pWriteBuffer; - - // The list of buffers that were written to by this thread. This - // is populated lazily the first time a thread tries to allocate - // a buffer for this session. It is set back to null when - // event writing is suspended during session disable. - // protected by the buffer manager lock - EventPipeBufferList* m_pBufferList; - -#ifdef DEBUG - // protected by the buffer manager lock - EventPipeBufferManager* m_pBufferManager; -#endif - - // The number of events that were attempted to be written by this - // thread. Each event was either succesfully recorded in a buffer - // or it was dropped. - // - // Only updated by the current thread under m_pThread::GetLock(). Other - // event writer threads are allowed to do unsychronized reads when - // capturing a sequence point but this does not provide any consistency - // guarantee. In particular there is no promise that the other thread - // is observing the most recent sequence number, nor is there a promise - // that the observable number of events in the write buffer matches the - // sequence number. A writer thread will always update the sequence - // number in tandem with an event write or drop, but without a write - // barrier between those memory writes they might observed out-of-order - // by the thread capturing the sequence point. The only utility this - // unsychronized read has is that if some other thread observes a sequence - // number X, it knows this thread must have attempted to write at least - // X events prior to the moment in time when the read occured. If the event - // buffers are later read and there are fewer than X events timestamped - // prior to the sequence point we can be certain the others were dropped. - Volatile m_sequenceNumber; - -public: - EventPipeThreadSessionState(EventPipeThread* pThread, EventPipeSession* pSession DEBUG_ARG(EventPipeBufferManager* pBufferManager)); - - EventPipeThread* GetThread(); - EventPipeSession* GetSession(); - EventPipeBuffer *GetWriteBuffer(); - void SetWriteBuffer(EventPipeBuffer *pNewBuffer); - EventPipeBufferList *GetBufferList(); - void SetBufferList(EventPipeBufferList *pBufferList); - unsigned int GetVolatileSequenceNumber(); - unsigned int GetSequenceNumber(); - void IncrementSequenceNumber(); -}; - -class EventPipeThread -{ - static thread_local EventPipeThreadHolder gCurrentEventPipeThreadHolder; - - static SpinLock s_threadsLock; - static SList> s_pThreads; - - ~EventPipeThread(); - - // The EventPipeThreadHolder maintains one count while the thread is alive - // and each session's EventPipeBufferList maintains one count while it - // exists - LONG m_refCount; - - // Per-session state. - // The pointers in this array are only read/written under m_lock - // Some of the data within the ThreadSessionState object can be accessed - // without m_lock however, see the fields of that type for details. - EventPipeThreadSessionState* m_sessionState[EventPipe::MaxNumberOfSessions]; - - // This lock is designed to have low contention. Normally it is only taken by this thread, - // but occasionally it may also be taken by another thread which is trying to collect and drain - // buffers from all threads. - SpinLock m_lock; - - // This is initialized when the Thread object is first constructed and remains - // immutable afterwards - SIZE_T m_osThreadId; - - // If this is set to a valid id before the corresponding entry of s_pSessions is set to null, - // that pointer will be protected from deletion. See EventPipe::DisableInternal() and - // EventPipe::WriteInternal for more detail. - Volatile m_writingEventInProgress; - - // - EventPipeSession *m_pRundownSession = nullptr; - - // Use Get/GetOrCreate instead - EventPipeThread(); - -public: - static void Initialize(); - - static EventPipeThread *Get(); - static EventPipeThread* GetOrCreate(); - - static EventPipeThreadIterator GetThreads(); - static SpinLock *GetGlobalThreadLock() - { - LIMITED_METHOD_CONTRACT; - - return &s_threadsLock; - } - - void AddRef(); - void Release(); - SpinLock *GetLock(); -#ifdef DEBUG - bool IsLockOwnedByCurrentThread(); -#endif - - EventPipeThreadSessionState* GetOrCreateSessionState(EventPipeSession* pSession); - EventPipeThreadSessionState* GetSessionState(EventPipeSession* pSession); - void DeleteSessionState(EventPipeSession* pSession); - SIZE_T GetOSThreadId(); - - bool IsRundownThread() const - { - LIMITED_METHOD_CONTRACT; - return (m_pRundownSession != nullptr); - } - - void SetAsRundownThread(EventPipeSession *pSession) - { - LIMITED_METHOD_CONTRACT; - m_pRundownSession = pSession; - } - - EventPipeSession *GetRundownSession() const - { - LIMITED_METHOD_CONTRACT; - return m_pRundownSession; - } - - void SetSessionWriteInProgress(uint32_t sessionIndex) - { - LIMITED_METHOD_CONTRACT; - m_writingEventInProgress.Store(sessionIndex); - } - - uint32_t GetSessionWriteInProgress() const - { - LIMITED_METHOD_CONTRACT; - return m_writingEventInProgress.Load(); - } -}; - -#endif // FEATURE_PERFTRACING - -#endif // __EVENTPIPE_THREAD_H__ diff --git a/src/coreclr/vm/fastserializer.cpp b/src/coreclr/vm/fastserializer.cpp deleted file mode 100644 index 238e5b772f2cc..0000000000000 --- a/src/coreclr/vm/fastserializer.cpp +++ /dev/null @@ -1,282 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "fastserializer.h" -#include "diagnosticsipc.h" -#include -#include - -#ifdef FEATURE_PERFTRACING - -// Event Pipe has previously implemented a feature called "forward references" -// As a result of work on V3 of Event Pipe (https://github.com/Microsoft/perfview/pull/532) it got removed -// if you need it, please use git to restore it - -IpcStreamWriter::IpcStreamWriter(uint64_t id, IpcStream *pStream) : _pStream(pStream) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(_pStream != nullptr); - } - CONTRACTL_END; - - if (_pStream == nullptr) - return; -} - -IpcStreamWriter::~IpcStreamWriter() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - delete _pStream; -} - -bool IpcStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - PRECONDITION(nBytesToWrite > 0); - } - CONTRACTL_END; - - if (_pStream == nullptr) - return false; - if (lpBuffer == nullptr || nBytesToWrite == 0) - return false; - return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten); -} - -FileStreamWriter::FileStreamWriter(const SString &outputFilePath) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - m_pFileStream = new CFileStream(); - if (FAILED(m_pFileStream->OpenForWrite(outputFilePath))) - { - delete m_pFileStream; - m_pFileStream = NULL; - return; - } -} - -FileStreamWriter::~FileStreamWriter() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - delete m_pFileStream; -} - -bool FileStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - PRECONDITION(nBytesToWrite > 0); - } - CONTRACTL_END; - - if (m_pFileStream == nullptr) - return false; - - ULONG outCount; - HRESULT hResult = m_pFileStream->Write(lpBuffer, nBytesToWrite, &outCount); - nBytesWritten = static_cast(outCount); - return hResult == S_OK; -} - -FastSerializer::FastSerializer(StreamWriter *pStreamWriter) : m_pStreamWriter(pStreamWriter) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(m_pStreamWriter != NULL); - } - CONTRACTL_END; - - m_writeErrorEncountered = false; - m_requiredPadding = 0; - WriteFileHeader(); -} - -FastSerializer::~FastSerializer() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - delete m_pStreamWriter; -} - -void FastSerializer::WriteObject(FastSerializableObject *pObject) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(pObject != NULL); - } - CONTRACTL_END; - - WriteTag(pObject->IsPrivate() ? FastSerializerTags::BeginPrivateObject : FastSerializerTags::BeginObject); - - WriteSerializationType(pObject); - - // Ask the object to serialize itself using the current serializer. - pObject->FastSerialize(this); - - WriteTag(FastSerializerTags::EndObject); -} - -void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pBuffer != NULL); - PRECONDITION(length > 0); - } - CONTRACTL_END; - - if (m_writeErrorEncountered || m_pStreamWriter == NULL) - return; - - EX_TRY - { - uint32_t outCount; - bool fSuccess = m_pStreamWriter->Write(pBuffer, length, outCount); - - m_requiredPadding = (ALIGNMENT_SIZE + m_requiredPadding - (outCount % ALIGNMENT_SIZE)) % ALIGNMENT_SIZE; - - // This will cause us to stop writing to the file. - // The file will still remain open until shutdown so that we don't - // have to take a lock at this level when we touch the file stream. - m_writeErrorEncountered = (length != outCount) || !fSuccess; - } - EX_CATCH - { - m_writeErrorEncountered = true; - } - EX_END_CATCH(SwallowAllExceptions); -} - -void FastSerializer::WriteSerializationType(FastSerializableObject *pObject) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(pObject != NULL); - } - CONTRACTL_END; - - // Write the BeginObject tag. - WriteTag(pObject->IsPrivate() ? FastSerializerTags::BeginPrivateObject : FastSerializerTags::BeginObject); - - // Write a NullReferenceTag, which implies that the following fields belong to SerializationType. - WriteTag(FastSerializerTags::NullReference); - - // Write the SerializationType version fields. - int serializationType[2] = { - pObject->GetObjectVersion(), - pObject->GetMinReaderVersion()}; - WriteBuffer((BYTE *)&serializationType, sizeof(serializationType)); - - // Write the SerializationType TypeName field. - const char *strTypeName = pObject->GetTypeName(); - unsigned int length = (unsigned int)strlen(strTypeName); - - WriteString(strTypeName, length); - - // Write the EndObject tag. - WriteTag(FastSerializerTags::EndObject); -} - -void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - WriteBuffer((BYTE *)&tag, sizeof(tag)); - if (payload != NULL) - { - _ASSERTE(payloadLength > 0); - WriteBuffer(payload, payloadLength); - } -} - -void FastSerializer::WriteFileHeader() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - const char *strSignature = "!FastSerialization.1"; // the consumer lib expects exactly the same string, it must not be changed - unsigned int length = (unsigned int)strlen(strSignature); - WriteString(strSignature, length); -} - -void FastSerializer::WriteString(const char *strContents, unsigned int length) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - // Write the string length . - WriteBuffer((BYTE *)&length, sizeof(length)); - - // Write the string contents. - WriteBuffer((BYTE *)strContents, length); -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/fastserializer.h b/src/coreclr/vm/fastserializer.h deleted file mode 100644 index 436813062cc87..0000000000000 --- a/src/coreclr/vm/fastserializer.h +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __FASTSERIALIZER_H__ -#define __FASTSERIALIZER_H__ - -#define ALIGNMENT_SIZE 4 - -#ifdef FEATURE_PERFTRACING - -#include "fastserializableobject.h" -#include "fstream.h" - -class IpcStream; - -// the enumeration has a specific set of values to keep it compatible with consumer library -// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295 -enum class FastSerializerTags : uint8_t -{ - Error = 0, // To improve debugabilty, 0 is an illegal tag. - NullReference = 1, // Tag for a null object forwardReference. - ObjectReference = 2, // Followed by StreamLabel - // 3 used to belong to ForwardReference, which got removed in V3 - BeginObject = 4, // Followed by Type object, object data, tagged EndObject - BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation - EndObject = 6, // Placed after an object to mark its end. - // 7 used to belong to ForwardDefinition, which got removed in V3 - Byte = 8, - Int16, - Int32, - Int64, - SkipRegion, - String, - Blob, - Limit // Just past the last valid tag, used for asserts. -}; - -//! -//! Provides a generic interface for writing a sequence of bytes to a stream. -//! -class StreamWriter -{ -public: - StreamWriter() = default; - virtual ~StreamWriter() = default; - virtual bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const = 0; -}; - -//! -//! Implements a StreamWriter for writing bytes to an IPC. -//! -class IpcStreamWriter final : public StreamWriter -{ -public: - IpcStreamWriter(uint64_t id, IpcStream *pStream); - ~IpcStreamWriter(); - bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const; - -private: - IpcStream *_pStream; -}; - -//! -//! Implements a StreamWriter for writing bytes to a File. -//! -class FileStreamWriter final : public StreamWriter -{ -public: - FileStreamWriter(const SString &outputFilePath); - ~FileStreamWriter(); - bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const; - -private: - CFileStream *m_pFileStream; -}; - -class FastSerializer -{ -public: - FastSerializer(StreamWriter *pStreamWriter); - ~FastSerializer(); - - void WriteObject(FastSerializableObject *pObject); - void WriteBuffer(BYTE *pBuffer, unsigned int length); - void WriteTag(FastSerializerTags tag, BYTE *payload = NULL, unsigned int payloadLength = 0); - void WriteString(const char *strContents, unsigned int length); - - unsigned int GetRequiredPadding() const - { - LIMITED_METHOD_CONTRACT; - return m_requiredPadding; - } - - bool HasWriteErrors() const - { - LIMITED_METHOD_CONTRACT; - return m_writeErrorEncountered; - } - -private: - void WriteSerializationType(FastSerializableObject *pObject); - void WriteFileHeader(); - - StreamWriter *const m_pStreamWriter; - bool m_writeErrorEncountered; - unsigned int m_requiredPadding; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __FASTSERIALIZER_H__ diff --git a/src/coreclr/vm/ipcstreamfactory.cpp b/src/coreclr/vm/ipcstreamfactory.cpp deleted file mode 100644 index dfaf4b93feaef..0000000000000 --- a/src/coreclr/vm/ipcstreamfactory.cpp +++ /dev/null @@ -1,372 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "diagnosticsprotocol.h" -#include "ipcstreamfactory.h" - -#ifdef FEATURE_PERFTRACING - -CQuickArrayList IpcStreamFactory::s_rgpDiagnosticPorts = CQuickArrayList(); -Volatile IpcStreamFactory::s_isShutdown = false; -IpcStreamFactory::DiagnosticPort *IpcStreamFactory::s_currentPort = nullptr; - -CQuickArrayList split(LPSTR string, LPCSTR delimiters) -{ - CQuickArrayList parts; - char *context; - char *part = nullptr; - for (char *cursor = string; ; cursor = nullptr) - { - if ((part = strtok_s(cursor, delimiters, &context)) != nullptr) - parts.Push(part); - else - break; - } - - return parts; -} - -bool IsWhitespace(char c) -{ - return (c == ' ' || c == '\r' || c == '\t' || c == '\n'); -} - -bool IsEmpty(LPCSTR string) -{ - uint32_t len = static_cast(strlen(string)); - for (uint32_t i = 0; i < len; i++) - if (!IsWhitespace(string[i])) - return false; - return true; -} - -bool IpcStreamFactory::ConnectDiagnosticPort::GetIpcPollHandle(IpcStream::DiagnosticsIpc::IpcPollHandle *pIpcPollHandle, ErrorCallback callback) -{ - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO1000, "IpcStreamFactory::ClientConnectionState::GetIpcPollHandle - ENTER.\n"); - if (_pStream == nullptr) - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::ClientConnectionState::GetIpcPollHandle - cache was empty!\n"); - // cache is empty, reconnect, e.g., there was a disconnect - IpcStream *pConnection = _pIpc->Connect(callback); - if (pConnection == nullptr) - { - if (callback != nullptr) - callback("Failed to connect to client connection", -1); - return false; - } -#ifdef TARGET_UNIX - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::ClientConnectionState::GetIpcPollHandle - returned connection { _clientSocket = %d }\n", pConnection->_clientSocket); -#else - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::ClientConnectionState::GetIpcPollHandle - returned connection { _hPipe = %d, _oOverlap.hEvent = %d }\n", pConnection->_hPipe, pConnection->_oOverlap.hEvent); -#endif - if (!DiagnosticsIpc::SendIpcAdvertise_V1(pConnection)) - { - if (callback != nullptr) - callback("Failed to send advertise message", -1); - delete pConnection; - return false; - } - - _pStream = pConnection; - } - *pIpcPollHandle = { nullptr, _pStream, 0, this }; - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::ClientConnectionState::GetIpcPollHandle - EXIT.\n"); - return true; -} - -IpcStream *IpcStreamFactory::ConnectDiagnosticPort::GetConnectedStream(ErrorCallback callback) -{ - IpcStream *pStream = _pStream; - _pStream = nullptr; - return pStream; -} - -void IpcStreamFactory::ConnectDiagnosticPort::Reset(ErrorCallback callback) -{ - delete _pStream; - _pStream = nullptr; -} - -bool IpcStreamFactory::ListenDiagnosticPort::GetIpcPollHandle(IpcStream::DiagnosticsIpc::IpcPollHandle *pIpcPollHandle, ErrorCallback callback) -{ - *pIpcPollHandle = { _pIpc, nullptr, 0, this }; - return true; -} - -IpcStream *IpcStreamFactory::ListenDiagnosticPort::GetConnectedStream(ErrorCallback callback) -{ - return _pIpc->Accept(callback); -} - -// noop for server -void IpcStreamFactory::ListenDiagnosticPort::Reset(ErrorCallback) -{ - return; -} - -bool IpcStreamFactory::Configure(ErrorCallback callback) -{ - bool fSuccess = true; - - NewArrayHolder dotnetDiagnosticPorts = nullptr; - CLRConfigStringHolder dotnetDiagnosticPortsW = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DOTNET_DiagnosticPorts); - int nCharactersWritten = 0; - if (dotnetDiagnosticPortsW != nullptr) - { - nCharactersWritten = WideCharToMultiByte(CP_UTF8, 0, dotnetDiagnosticPortsW, -1, NULL, 0, NULL, NULL); - dotnetDiagnosticPorts = new char[nCharactersWritten]; - nCharactersWritten = WideCharToMultiByte(CP_UTF8, 0, dotnetDiagnosticPortsW, -1, dotnetDiagnosticPorts, nCharactersWritten, NULL, NULL); - ASSERT(nCharactersWritten != 0); - - CQuickArrayList portConfigs = split(dotnetDiagnosticPorts, ";"); - while (portConfigs.Size() > 0) - { - LPSTR portConfig = portConfigs.Pop(); - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::Configure - Attempted to create Diagnostic Port from \"%s\".\n", portConfig); - CQuickArrayList portConfigParts = split(portConfig, ","); - DiagnosticPortBuilder builder; - - if (portConfigParts.Size() == 0) - { - fSuccess &= false; - continue; - } - - while (portConfigParts.Size() > 1) - builder.WithTag(portConfigParts.Pop()); - builder.WithPath(portConfigParts.Pop()); - - if (IsEmpty(builder.Path)) - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::Configure - Ignoring port configuration with empty address\n"); - continue; - } - - // Ignore listen type (see conversation in https://github.com/dotnet/runtime/pull/40499 for details) - if (builder.Type == DiagnosticPortType::LISTEN) - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::Configure - Ignoring LISTEN port configuration \n"); - continue; - } - - const bool fBuildSuccess = BuildAndAddPort(builder, callback); - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::Configure - Diagnostic Port creation succeeded? %d \n", fBuildSuccess); - fSuccess &= fBuildSuccess; - } - } - - // create the default listen port - DWORD dotnetDiagnosticPortSuspend = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend); - DiagnosticPortBuilder defaultListenPortBuilder = DiagnosticPortBuilder{} - .WithPath(nullptr) - .WithSuspendMode(dotnetDiagnosticPortSuspend > 0 ? DiagnosticPortSuspendMode::SUSPEND : DiagnosticPortSuspendMode::NOSUSPEND) - .WithType(DiagnosticPortType::LISTEN); - - - fSuccess &= BuildAndAddPort(defaultListenPortBuilder, callback); - return fSuccess; -} - -bool IpcStreamFactory::BuildAndAddPort(IpcStreamFactory::DiagnosticPortBuilder builder, ErrorCallback callback) -{ - if (builder.Type == DiagnosticPortType::LISTEN) - { - IpcStream::DiagnosticsIpc *pIpc = IpcStream::DiagnosticsIpc::Create(builder.Path, IpcStream::DiagnosticsIpc::ConnectionMode::LISTEN, callback); - if (pIpc != nullptr) - { - if (pIpc->Listen(callback)) - { - s_rgpDiagnosticPorts.Push(new ListenDiagnosticPort(pIpc, builder)); - return true; - } - else - { - delete pIpc; - return false; - } - } - else - { - return false; - } - } - else if (builder.Type == DiagnosticPortType::CONNECT) - { - IpcStream::DiagnosticsIpc *pIpc = IpcStream::DiagnosticsIpc::Create(builder.Path, IpcStream::DiagnosticsIpc::ConnectionMode::CONNECT, callback); - if (pIpc != nullptr) - { - s_rgpDiagnosticPorts.Push(new ConnectDiagnosticPort(pIpc, builder)); - return true; - } - else - { - return false; - } - } - return false; -} - -bool IpcStreamFactory::HasActivePorts() -{ - return !s_isShutdown && s_rgpDiagnosticPorts.Size() > 0; -} - -void IpcStreamFactory::ClosePorts(ErrorCallback callback) -{ - for (uint32_t i = 0; i < (uint32_t)s_rgpDiagnosticPorts.Size(); i++) - s_rgpDiagnosticPorts[i]->Close(callback); -} - -void IpcStreamFactory::Shutdown(ErrorCallback callback) -{ - if (s_isShutdown) - return; - s_isShutdown = true; - for (uint32_t i = 0; i < (uint32_t)s_rgpDiagnosticPorts.Size(); i++) - s_rgpDiagnosticPorts[i]->Close(true, callback); -} - -bool IpcStreamFactory::AnySuspendedPorts() -{ - bool fAnySuspendedPorts = false; - for (uint32_t i = 0; i < (uint32_t)s_rgpDiagnosticPorts.Size(); i++) - fAnySuspendedPorts |= !(s_rgpDiagnosticPorts[i]->SuspendMode == DiagnosticPortSuspendMode::NOSUSPEND || s_rgpDiagnosticPorts[i]->HasResumedRuntime); - return fAnySuspendedPorts; -} - -void IpcStreamFactory::ResumeCurrentPort() -{ - if (s_currentPort != nullptr) - s_currentPort->HasResumedRuntime = true; -} - -// helper function for getting timeout -int32_t IpcStreamFactory::GetNextTimeout(int32_t currentTimeoutMs) -{ - if (currentTimeoutMs == s_pollTimeoutInfinite) - { - return s_pollTimeoutMinMs; - } - else - { - return (currentTimeoutMs >= s_pollTimeoutMaxMs) ? - s_pollTimeoutMaxMs : - (int32_t)((float)currentTimeoutMs * s_pollTimeoutFalloffFactor); - } -} - -IpcStream *IpcStreamFactory::GetNextAvailableStream(ErrorCallback callback) -{ - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - ENTER"); - IpcStream *pStream = nullptr; - CQuickArrayList rgIpcPollHandles; - - int32_t pollTimeoutMs = s_pollTimeoutInfinite; - bool fConnectSuccess = true; - uint32_t nPollAttempts = 0; - - while (pStream == nullptr) - { - fConnectSuccess = true; - for (uint32_t i = 0; i < (uint32_t)s_rgpDiagnosticPorts.Size(); i++) - { - IpcStream::DiagnosticsIpc::IpcPollHandle pollHandle = {}; - if (s_rgpDiagnosticPorts[i]->GetIpcPollHandle(&pollHandle, callback)) - { - rgIpcPollHandles.Push(pollHandle); - } - else - { - fConnectSuccess = false; - } - } - - pollTimeoutMs = fConnectSuccess ? - s_pollTimeoutInfinite : - GetNextTimeout(pollTimeoutMs); - - nPollAttempts++; - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - Poll attempt: %d, timeout: %dms.\n", nPollAttempts, pollTimeoutMs); - for (uint32_t i = 0; i < rgIpcPollHandles.Size(); i++) - { - if (rgIpcPollHandles[i].pIpc != nullptr) - { -#ifdef TARGET_UNIX - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "\tSERVER IpcPollHandle[%d] = { _serverSocket = %d }\n", i, rgIpcPollHandles[i].pIpc->_serverSocket); -#else - STRESS_LOG3(LF_DIAGNOSTICS_PORT, LL_INFO10, "\tSERVER IpcPollHandle[%d] = { _hPipe = %d, _oOverlap.hEvent = %d }\n", i, rgIpcPollHandles[i].pIpc->_hPipe, rgIpcPollHandles[i].pIpc->_oOverlap.hEvent); -#endif - } - else - { -#ifdef TARGET_UNIX - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "\tCLIENT IpcPollHandle[%d] = { _clientSocket = %d }\n", i, rgIpcPollHandles[i].pStream->_clientSocket); -#else - STRESS_LOG3(LF_DIAGNOSTICS_PORT, LL_INFO10, "\tCLIENT IpcPollHandle[%d] = { _hPipe = %d, _oOverlap.hEvent = %d }\n", i, rgIpcPollHandles[i].pStream->_hPipe, rgIpcPollHandles[i].pStream->_oOverlap.hEvent); -#endif - } - } - int32_t retval = IpcStream::DiagnosticsIpc::Poll(rgIpcPollHandles.Ptr(), (uint32_t)rgIpcPollHandles.Size(), pollTimeoutMs, callback); - bool fSawError = false; - - if (retval != 0) - { - for (uint32_t i = 0; i < (uint32_t)rgIpcPollHandles.Size(); i++) - { - switch ((IpcStream::DiagnosticsIpc::PollEvents)rgIpcPollHandles[i].revents) - { - case IpcStream::DiagnosticsIpc::PollEvents::HANGUP: - ((DiagnosticPort*)(rgIpcPollHandles[i].pUserData))->Reset(callback); - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - HUP :: Poll attempt: %d, connection %d hung up. Connect is reset.\n", nPollAttempts, i); - pollTimeoutMs = s_pollTimeoutMinMs; - break; - case IpcStream::DiagnosticsIpc::PollEvents::SIGNALED: - if (pStream == nullptr) // only use first signaled stream; will get others on subsequent calls - { - pStream = ((DiagnosticPort*)(rgIpcPollHandles[i].pUserData))->GetConnectedStream(callback); - if (pStream == nullptr) - { - fSawError = true; - } - s_currentPort = (DiagnosticPort*)(rgIpcPollHandles[i].pUserData); - } - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - SIG :: Poll attempt: %d, connection %d signalled.\n", nPollAttempts, i); - break; - case IpcStream::DiagnosticsIpc::PollEvents::ERR: - ((DiagnosticPort*)(rgIpcPollHandles[i].pUserData))->Reset(callback); - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - ERR :: Poll attempt: %d, connection %d errored. Connection is reset.\n", nPollAttempts, i); - fSawError = true; - break; - case IpcStream::DiagnosticsIpc::PollEvents::NONE: - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - NON :: Poll attempt: %d, connection %d had no events.\n", nPollAttempts, i); - break; - default: - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - UNK :: Poll attempt: %d, connection %d had invalid PollEvent.\n", nPollAttempts, i); - fSawError = true; - break; - } - } - } - - - if (pStream == nullptr && fSawError) - { - s_currentPort = nullptr; - return nullptr; - } - - // clear the view - while (rgIpcPollHandles.Size() > 0) - rgIpcPollHandles.Pop(); - } - -#ifdef TARGET_UNIX - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - EXIT :: Poll attempt: %d, stream using handle %d.\n", nPollAttempts, pStream->_clientSocket); -#else - STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::GetNextAvailableStream - EXIT :: Poll attempt: %d, stream using handle %d.\n", nPollAttempts, pStream->_hPipe); -#endif - return pStream; -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/ipcstreamfactory.h b/src/coreclr/vm/ipcstreamfactory.h deleted file mode 100644 index 00acbf6682483..0000000000000 --- a/src/coreclr/vm/ipcstreamfactory.h +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __IPC_STREAM_FACTORY_H__ -#define __IPC_STREAM_FACTORY_H__ - -#ifdef FEATURE_PERFTRACING - -#include "diagnosticsipc.h" - -class IpcStreamFactory -{ -public: - // forward declare - struct DiagnosticPort; - - enum class DiagnosticPortType : uint8_t - { - LISTEN = 0, - CONNECT = 1 - }; - - enum class DiagnosticPortSuspendMode : uint8_t - { - NOSUSPEND = 0, - SUSPEND = 1 - }; - - struct DiagnosticPortBuilder - { - LPSTR Path = nullptr; - DiagnosticPortType Type = DiagnosticPortType::CONNECT; - DiagnosticPortSuspendMode SuspendMode = DiagnosticPortSuspendMode::SUSPEND; - - DiagnosticPortBuilder WithPath(LPSTR path) { Path = path; return *this; } - DiagnosticPortBuilder WithType(DiagnosticPortType type) { Type = type; return *this; } - DiagnosticPortBuilder WithSuspendMode(DiagnosticPortSuspendMode mode) { SuspendMode = mode; return *this; } - DiagnosticPortBuilder WithTag(LPSTR tag) - { - // check if port type - if (_stricmp(tag, "listen") == 0) - return WithType(DiagnosticPortType::LISTEN); - - if (_stricmp(tag, "connect") == 0) - return WithType(DiagnosticPortType::CONNECT); - - // check if suspendmode tag - if (_stricmp(tag, "nosuspend") == 0) - return WithSuspendMode(DiagnosticPortSuspendMode::NOSUSPEND); - - if (_stricmp(tag, "suspend") == 0) - return WithSuspendMode(DiagnosticPortSuspendMode::SUSPEND); - - // don't mutate if it's not a valid option - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, "IpcStreamFactory::DiagnosticPortBuilder::WithTag - Unknown tag '%s'.\n", tag); - return *this; - } - }; - - struct DiagnosticPort - { - public: - DiagnosticPort(IpcStream::DiagnosticsIpc *pIpc, DiagnosticPortBuilder builder) : - SuspendMode(builder.SuspendMode), - _pIpc(pIpc), - _pStream(nullptr), - _type(builder.Type) - { } - - const DiagnosticPortSuspendMode SuspendMode; - - // Will be false until ResumeRuntime command is sent on this connection - bool HasResumedRuntime = false; - - // returns a pollable handle and performs any preparation required - // e.g., as a side-effect, will connect and advertise on reverse connections - virtual bool GetIpcPollHandle(IpcStream::DiagnosticsIpc::IpcPollHandle *pIpcPollHandle, ErrorCallback callback = nullptr) = 0; - - // Returns the signaled stream in a usable state - virtual IpcStream *GetConnectedStream(ErrorCallback callback = nullptr) = 0; - - // Resets the connection in the event of a hangup - virtual void Reset(ErrorCallback callback = nullptr) = 0; - - // closes the underlying connections - // only performs minimal cleanup if isShutdown==true - void Close(bool isShutdown = false, ErrorCallback callback = nullptr) - { - if (_pIpc != nullptr) - _pIpc->Close(isShutdown, callback); - if (_pStream != nullptr && !isShutdown) - _pStream->Close(callback); - } - - protected: - IpcStream::DiagnosticsIpc *_pIpc; - IpcStream *_pStream; - DiagnosticPortType _type; - }; - - struct ConnectDiagnosticPort : public DiagnosticPort - { - ConnectDiagnosticPort(IpcStream::DiagnosticsIpc *pIpc, DiagnosticPortBuilder builder) : DiagnosticPort(pIpc, builder) { } - - // returns a pollable handle and performs any preparation required - bool GetIpcPollHandle(IpcStream::DiagnosticsIpc::IpcPollHandle *pIpcPollHandle, ErrorCallback callback = nullptr) override; - - // Returns the signaled stream in a usable state - IpcStream *GetConnectedStream(ErrorCallback callback = nullptr) override; - - // Resets the connection in the event of a hangup - void Reset(ErrorCallback callback = nullptr) override; - }; - - struct ListenDiagnosticPort : public DiagnosticPort - { - ListenDiagnosticPort(IpcStream::DiagnosticsIpc *pIpc, DiagnosticPortBuilder builder) : DiagnosticPort(pIpc, builder) { } - - // returns a pollable handle and performs any preparation required - bool GetIpcPollHandle(IpcStream::DiagnosticsIpc::IpcPollHandle *pIpcPollHandle, ErrorCallback callback = nullptr) override; - - // Returns the signaled stream in a usable state - IpcStream *GetConnectedStream(ErrorCallback callback = nullptr) override; - - // Resets the connection in the event of a hangup - void Reset(ErrorCallback callback = nullptr) override; - }; - - static bool Configure(ErrorCallback callback = nullptr); - static IpcStream *GetNextAvailableStream(ErrorCallback = nullptr); - static void ResumeCurrentPort(); - static bool AnySuspendedPorts(); - static bool HasActivePorts(); - static void ClosePorts(ErrorCallback callback = nullptr); - static void Shutdown(ErrorCallback callback = nullptr); -private: - static bool BuildAndAddPort(DiagnosticPortBuilder builder, ErrorCallback callback = nullptr); - static CQuickArrayList s_rgpDiagnosticPorts; - static Volatile s_isShutdown; - // set this in GetNextAvailableStream, and then expose a callback that - // allows us to track which connections have sent their ResumeRuntime commands - static DiagnosticPort *s_currentPort; - - // Polling timeout semantics - // If client connection is opted in - // and connection succeeds => set timeout to infinite - // and connection fails => set timeout to minimum and scale by falloff factor - // else => set timeout to -1 (infinite) - // - // If an agent closes its socket while we're still connected, - // Poll will return and let us know which connection hung up - static int32_t GetNextTimeout(int32_t currentTimeoutMs); - constexpr static float s_pollTimeoutFalloffFactor = 1.25; - constexpr static int32_t s_pollTimeoutInfinite = -1; - constexpr static int32_t s_pollTimeoutMinMs = 10; - constexpr static int32_t s_pollTimeoutMaxMs = 500; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __IPC_STREAM_FACTORY_H__ diff --git a/src/coreclr/vm/processdiagnosticsprotocolhelper.cpp b/src/coreclr/vm/processdiagnosticsprotocolhelper.cpp deleted file mode 100644 index 272cce094480b..0000000000000 --- a/src/coreclr/vm/processdiagnosticsprotocolhelper.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "fastserializer.h" -#include "processdiagnosticsprotocolhelper.h" -#include "eventpipeeventsource.h" -#include "diagnosticsprotocol.h" -#include "diagnosticserver.h" - -#ifdef FEATURE_PERFTRACING - -static inline uint32_t GetStringLength(const WCHAR *value) -{ - return static_cast(wcslen(value) + 1); -} - -static bool TryWriteString(uint8_t * &bufferCursor, uint16_t &bufferLen, const WCHAR *value) -{ - uint32_t stringLen = GetStringLength(value); - S_UINT16 totalStringSizeInBytes(stringLen * sizeof(WCHAR) + sizeof(uint32_t)); - ASSERT(!totalStringSizeInBytes.IsOverflow()); - ASSERT(bufferLen >= totalStringSizeInBytes.Value()); - if (bufferLen < totalStringSizeInBytes.Value() || totalStringSizeInBytes.IsOverflow()) - return false; - - memcpy(bufferCursor, &stringLen, sizeof(stringLen)); - bufferCursor += sizeof(stringLen); - - memcpy(bufferCursor, value, stringLen * sizeof(WCHAR)); - bufferCursor += stringLen * sizeof(WCHAR); - - bufferLen -= totalStringSizeInBytes.Value(); - return true; -} - -static bool TryWriteString(IpcStream *pStream, const WCHAR *value) -{ - uint32_t stringLen = GetStringLength(value); - uint32_t stringLenInBytes = stringLen * sizeof(WCHAR); - uint32_t totalStringSizeInBytes = stringLenInBytes + sizeof(uint32_t); - - uint32_t totalBytesWritten = 0; - uint32_t nBytesWritten = 0; - - bool fSuccess = pStream->Write(&stringLen, sizeof(stringLen), nBytesWritten); - totalBytesWritten += nBytesWritten; - - if (fSuccess) - { - fSuccess &= pStream->Write(value, stringLenInBytes, nBytesWritten); - totalBytesWritten += nBytesWritten; - } - - ASSERT(totalBytesWritten == totalStringSizeInBytes); - return fSuccess && totalBytesWritten == totalStringSizeInBytes; -} - -uint16_t ProcessInfoPayload::GetSize() -{ - LIMITED_METHOD_CONTRACT; - - // see IPC spec @ https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md - // for definition of serialization format - - // uint64_t ProcessId; -> 8 bytes - // GUID RuntimeCookie; -> 16 bytes - // LPCWSTR CommandLine; -> 4 bytes + strlen * sizeof(WCHAR) - // LPCWSTR OS; -> 4 bytes + strlen * sizeof(WCHAR) - // LPCWSTR Arch; -> 4 bytes + strlen * sizeof(WCHAR) - - S_UINT16 size = S_UINT16(0); - size += sizeof(ProcessId); - size += sizeof(RuntimeCookie); - - size += sizeof(uint32_t); - size += CommandLine != nullptr ? - S_UINT16(GetStringLength(CommandLine) * sizeof(WCHAR)) : - S_UINT16(0); - - size += sizeof(uint32_t); - size += OS != nullptr ? - S_UINT16(GetStringLength(OS) * sizeof(WCHAR)) : - S_UINT16(0); - - size += sizeof(uint32_t); - size += Arch != nullptr ? - S_UINT16(GetStringLength(Arch) * sizeof(WCHAR)) : - S_UINT16(0); - - ASSERT(!size.IsOverflow()); - return size.Value(); -} - -bool ProcessInfoPayload::Flatten(BYTE * &lpBuffer, uint16_t &cbSize) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(cbSize == GetSize()); - PRECONDITION(lpBuffer != nullptr); - } - CONTRACTL_END; - - // see IPC spec @ https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md - // for definition of serialization format - - bool fSuccess = true; - // uint64_t ProcessId; - memcpy(lpBuffer, &ProcessId, sizeof(ProcessId)); - lpBuffer += sizeof(ProcessId); - cbSize -= sizeof(ProcessId); - - // GUID RuntimeCookie; - memcpy(lpBuffer, &RuntimeCookie, sizeof(RuntimeCookie)); - lpBuffer += sizeof(RuntimeCookie); - cbSize -= sizeof(RuntimeCookie); - - // LPCWSTR CommandLine; - fSuccess &= TryWriteString(lpBuffer, cbSize, CommandLine); - - // LPCWSTR OS; - if (fSuccess) - fSuccess &= TryWriteString(lpBuffer, cbSize, OS); - - // LPCWSTR Arch; - if (fSuccess) - fSuccess &= TryWriteString(lpBuffer, cbSize, Arch); - - // Assert we've used the whole buffer we were given - ASSERT(cbSize == 0); - - return fSuccess; -} - -void EnvironmentHelper::PopulateEnvironment() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - if (Environment != nullptr) - return; - - // env block is an array of strings of the form "key=value" delimited by null and terminated by null - // e.g., "key=value\0key=value\0\0"; - LPWSTR envBlock = GetEnvironmentStringsW(); - LPWSTR envCursor = envBlock; - // first calculate the buffer size - uint32_t nWchars = 0; - uint32_t nEntries = 0; - while(*envCursor != 0) - { - uint32_t len = static_cast(wcslen(envCursor) + 1); - nEntries++; - nWchars += len; - envCursor += len; - } - - // serialized size value can't be larger than uint32_t - S_UINT32 size(sizeof(uint32_t) + (nEntries * sizeof(uint32_t)) + (nWchars * sizeof(WCHAR))); - // if the envblock size value is larger than a uint32_t then we should bail - if (!size.IsOverflow()) - { - // copy out the Environment block - LPWSTR tmpEnv = new (nothrow) WCHAR[nWchars + 1]; - if (tmpEnv != nullptr) - { - memcpy(tmpEnv, envBlock, (nWchars + 1) * sizeof(WCHAR)); - Environment = tmpEnv; - } - _nEnvEntries = nEntries; - _nWchars = nWchars; - } - - FreeEnvironmentStringsW(envBlock); - - return; -} - -uint32_t EnvironmentHelper::GetEnvironmentBlockSize() -{ - LIMITED_METHOD_CONTRACT; - - S_UINT32 size(sizeof(uint32_t) + (_nEnvEntries * sizeof(uint32_t)) + (_nWchars * sizeof(WCHAR))); - return size.IsOverflow() ? sizeof(uint32_t) : size.Value(); -} - -bool EnvironmentHelper::WriteToStream(IpcStream *pStream) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - PRECONDITION(Environment != nullptr); - } - CONTRACTL_END; - - // Array> - uint32_t nBytesWritten = 0; - bool fSuccess = pStream->Write(&_nEnvEntries, sizeof(_nEnvEntries), nBytesWritten); - - LPCWSTR cursor = Environment; - for (uint32_t i = 0; i < _nEnvEntries; i++) - { - if (!fSuccess || cursor == nullptr) - break; - - uint32_t len = static_cast(wcslen(cursor) + 1); - fSuccess &= TryWriteString(pStream, cursor); - cursor += len; - } - - return fSuccess; -} - -void ProcessDiagnosticsProtocolHelper::GetProcessEnvironment(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - // GetProcessEnvironment responds with a Diagnostics IPC message - // who's payload is a uint32_t that specifies the number of bytes - // that will follow. The optional continuation will contain - // the Environemnt streamed in the standard Diagnostics IPC format (length-prefixed arrays). - - struct EnvironmentHelper helper = {}; - helper.PopulateEnvironment(); - - struct EnvironmentHelper::InitialPayload payload = { helper.GetEnvironmentBlockSize(), 0 }; - - DiagnosticsIpc::IpcMessage ProcessEnvironmentResponse; - const bool fSuccess = ProcessEnvironmentResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, payload) ? - ProcessEnvironmentResponse.Send(pStream) : - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - - if (!fSuccess) - { - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Failed to send DiagnosticsIPC response"); - } - else - { - // send the environment - const bool fSuccessWriteEnv = helper.WriteToStream(pStream); - if (!fSuccessWriteEnv) - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Failed to stream environment"); - } - - delete pStream; -} - -void ProcessDiagnosticsProtocolHelper::GetProcessInfo(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - struct ProcessInfoPayload payload = {}; - - // Get cmdline - payload.CommandLine = GetCommandLineForDiagnostics(); - - // get OS + Arch info - payload.OS = EventPipeEventSource::s_pOSInformation; - payload.Arch = EventPipeEventSource::s_pArchInformation; - - // Get the PID - payload.ProcessId = GetCurrentProcessId(); - - // Get the cookie - payload.RuntimeCookie = DiagnosticsIpc::GetAdvertiseCookie_V1(); - - DiagnosticsIpc::IpcMessage ProcessInfoResponse; - const bool fSuccess = ProcessInfoResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, payload) ? - ProcessInfoResponse.Send(pStream) : - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - - if (!fSuccess) - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_WARNING, "Failed to send DiagnosticsIPC response"); - - delete pStream; -} - -void ProcessDiagnosticsProtocolHelper::ResumeRuntimeStartup(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - // no payload - DiagnosticServer::ResumeRuntimeStartup(); - HRESULT res = S_OK; - - DiagnosticsIpc::IpcMessage successResponse; - const bool fSuccess = successResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, res) ? - successResponse.Send(pStream) : - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL); - if (!fSuccess) - STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_WARNING, "Failed to send DiagnosticsIPC response"); - - delete pStream; -} - -void ProcessDiagnosticsProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - switch ((ProcessCommandId)message.GetHeader().CommandId) - { - case ProcessCommandId::GetProcessInfo: - ProcessDiagnosticsProtocolHelper::GetProcessInfo(message, pStream); - break; - case ProcessCommandId::ResumeRuntime: - ProcessDiagnosticsProtocolHelper::ResumeRuntimeStartup(message, pStream); - break; - case ProcessCommandId::GetProcessEnvironment: - ProcessDiagnosticsProtocolHelper::GetProcessEnvironment(message, pStream); - break; - - default: - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet); - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND); - delete pStream; - break; - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/processdiagnosticsprotocolhelper.h b/src/coreclr/vm/processdiagnosticsprotocolhelper.h deleted file mode 100644 index fe75369081f22..0000000000000 --- a/src/coreclr/vm/processdiagnosticsprotocolhelper.h +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __PROCESS_PROTOCOL_HELPER_H__ -#define __PROCESS_PROTOCOL_HELPER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include "eventpipe.h" -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -class IpcStream; - -// The event pipe command set is 0x02 -// see diagnosticsipc.h and diagnosticserver.h for more details -enum class ProcessCommandId : uint8_t -{ - GetProcessInfo = 0x00, - ResumeRuntime = 0x01, - GetProcessEnvironment = 0x02 - // future -}; - -// command = 0x0400 -struct ProcessInfoPayload -{ - // The protocol buffer is defined as: - // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z - // uint = 4 little endian bytes - // long = 8 little endian bytes - // GUID = 16 little endian bytes - // wchar = 2 little endian bytes, UTF16 encoding - // array = uint length, length # of Ts - - // ProcessInfo = long pid, string cmdline, string OS, string arch, GUID runtimeCookie - uint64_t ProcessId; - LPCWSTR CommandLine; - LPCWSTR OS; - LPCWSTR Arch; - GUID RuntimeCookie; - uint16_t GetSize(); - bool Flatten(BYTE * &lpBuffer, uint16_t& cbSize); -}; - -struct EnvironmentHelper -{ - // The environemnt is sent back as an optional continuation stream of data. - // It is encoded in the typical length-prefixed array format as defined in - // the Diagnostics IPC Spec: https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md - - struct InitialPayload - { - uint32_t continuationSizeInBytes; - uint16_t future; - }; - - // sent as: Array> - NewArrayHolder Environment = nullptr; - - void PopulateEnvironment(); - uint32_t GetNumberOfElements() { PopulateEnvironment(); return _nEnvEntries; } - - // Write the environment block to the stream - bool WriteToStream(IpcStream *pStream); - - // The size in bytes of the Diagnostic IPC Protocol encoded Environment Block - // It is encoded as Array> so this will return at least sizeof(uint32_t) - // if the env block is empty or failed to be snapshotted since the stream will - // just contain 0 for the array length. - uint32_t GetEnvironmentBlockSize(); -private: - uint32_t _nEnvEntries = 0; - uint32_t _nWchars = 0; -}; - -class ProcessDiagnosticsProtocolHelper -{ -public: - // IPC event handlers. - static void HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static void GetProcessInfo(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static void GetProcessEnvironment(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - static void ResumeRuntimeStartup(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); -}; - -#endif // FEATURE_PERFTRACING - -#endif // __PROCESS_PROTOCOL_HELPER_H__ diff --git a/src/coreclr/vm/profilerdiagnosticprotocolhelper.cpp b/src/coreclr/vm/profilerdiagnosticprotocolhelper.cpp deleted file mode 100644 index efa01fc051646..0000000000000 --- a/src/coreclr/vm/profilerdiagnosticprotocolhelper.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "fastserializer.h" -#include "profilerdiagnosticprotocolhelper.h" -#include "diagnosticsipc.h" -#include "diagnosticsprotocol.h" - -#if defined(FEATURE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) && !defined(DACCESS_COMPILE) -#include "profilinghelper.h" -#include "profilinghelper.inl" - -void ProfilerDiagnosticProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - if (!g_fEEStarted) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORPROF_E_NOT_YET_AVAILABLE); - delete pStream; - return; - } - - switch ((ProfilerCommandId)message.GetHeader().CommandId) - { - case ProfilerCommandId::AttachProfiler: - ProfilerDiagnosticProtocolHelper::AttachProfiler(message, pStream); - break; - - default: - STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet); - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND); - delete pStream; - break; - } -} - -const AttachProfilerCommandPayload* AttachProfilerCommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(lpBuffer != nullptr); - } - CONTRACTL_END; - - AttachProfilerCommandPayload* payload = new (nothrow) AttachProfilerCommandPayload; - if (payload == nullptr) - { - // OOM - return nullptr; - } - - payload->incomingBuffer = lpBuffer; - uint8_t* pBufferCursor = payload->incomingBuffer; - uint32_t bufferLen = BufferSize; - if (!::TryParse(pBufferCursor, bufferLen, payload->dwAttachTimeout) || - !::TryParse(pBufferCursor, bufferLen, payload->profilerGuid) || - !TryParseString(pBufferCursor, bufferLen, payload->pwszProfilerPath) || - !::TryParse(pBufferCursor, bufferLen, payload->cbClientData) || - !(bufferLen <= payload->cbClientData)) - { - delete payload; - return nullptr; - } - - payload->pClientData = pBufferCursor; - - return payload; -} - -void ProfilerDiagnosticProtocolHelper::AttachProfiler(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(pStream != nullptr); - } - CONTRACTL_END; - - if (pStream == nullptr) - { - return; - } - - HRESULT hr = S_OK; - NewHolder payload = message.TryParsePayload(); - if (payload == nullptr) - { - hr = CORDIAGIPC_E_BAD_ENCODING; - goto ErrExit; - } - - if (!g_profControlBlock.fProfControlBlockInitialized) - { - hr = CORPROF_E_RUNTIME_UNINITIALIZED; - goto ErrExit; - } - - // Certain actions are only allowable during attach, and this flag is how we track it. - ClrFlsSetThreadType(ThreadType_ProfAPI_Attach); - - EX_TRY - { - hr = ProfilingAPIUtility::LoadProfilerForAttach(&payload->profilerGuid, - payload->pwszProfilerPath, - payload->pClientData, - payload->cbClientData, - payload->dwAttachTimeout); - } - EX_CATCH_HRESULT(hr); - - // Clear the flag so this thread isn't permanently marked as the attach thread. - ClrFlsClearThreadType(ThreadType_ProfAPI_Attach); - -ErrExit: - if (hr != S_OK) - { - DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, hr); - } - else - { - DiagnosticsIpc::IpcMessage profilerAttachResponse; - if (profilerAttachResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, hr)) - profilerAttachResponse.Send(pStream); - } - delete pStream; -} - -#endif // defined(FEATURE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) && !defined(DACCESS_COMPILE) diff --git a/src/coreclr/vm/profilerdiagnosticprotocolhelper.h b/src/coreclr/vm/profilerdiagnosticprotocolhelper.h deleted file mode 100644 index e22f7cb9a7249..0000000000000 --- a/src/coreclr/vm/profilerdiagnosticprotocolhelper.h +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __PROFILER_DIAGNOSTIC_PROTOCOL_HELPER_H__ -#define __PROFILER_DIAGNOSTIC_PROTOCOL_HELPER_H__ - -#if defined(FEATURE_PROFAPI_ATTACH_DETACH) && defined(FEATURE_PERFTRACING) - -#include "common.h" -#include - - -class IpcStream; - -// The Diagnostic command set is 0x01 -enum class ProfilerCommandId : uint8_t -{ - // reserved = 0x00, - AttachProfiler = 0x01, - // future -}; - -struct AttachProfilerCommandPayload -{ - NewArrayHolder incomingBuffer; - - // The protocol buffer is defined as: - // uint - attach timeout - // CLSID - profiler GUID - // string - profiler path - // array - client data - // returns - // ulong - status - - uint32_t dwAttachTimeout; - CLSID profilerGuid; - LPCWSTR pwszProfilerPath; - uint32_t cbClientData; - uint8_t* pClientData; - - static const AttachProfilerCommandPayload* TryParse(BYTE* lpBuffer, uint16_t& BufferSize); -}; - -class ProfilerDiagnosticProtocolHelper -{ -public: - // IPC event handlers. - static void HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream); - static void AttachProfiler(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); - -private: - const static uint32_t IpcStreamReadBufferSize = 8192; -}; - -#endif // defined(FEATURE_PROFAPI_ATTACH_DETACH) && defined(FEATURE_PERFTRACING) - -#endif // __PROFILER_DIAGNOSTIC_PROTOCOL_HELPER_H__ diff --git a/src/coreclr/vm/sampleprofiler.cpp b/src/coreclr/vm/sampleprofiler.cpp deleted file mode 100644 index 2714463ed92e9..0000000000000 --- a/src/coreclr/vm/sampleprofiler.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "eventpipebuffermanager.h" -#include "eventpipeeventinstance.h" -#include "sampleprofiler.h" -#include "threadsuspend.h" - -#ifdef FEATURE_PERFTRACING - -#ifndef TARGET_UNIX -#include -#endif //TARGET_UNIX - -const unsigned long NUM_NANOSECONDS_IN_1_MS = 1000000; - -Volatile SampleProfiler::s_profilingEnabled = FALSE; -Thread *SampleProfiler::s_pSamplingThread = NULL; -const WCHAR *SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler"); -EventPipeProvider *SampleProfiler::s_pEventPipeProvider = NULL; -EventPipeEvent *SampleProfiler::s_pThreadTimeEvent = NULL; -SampleProfiler::SampleProfilerPayload SampleProfiler::s_ExternalPayload = {SampleProfilerSampleType::External}; -SampleProfiler::SampleProfilerPayload SampleProfiler::s_ManagedPayload = {SampleProfilerSampleType::Managed}; -CLREventStatic SampleProfiler::s_threadShutdownEvent; -unsigned long SampleProfiler::s_samplingRateInNs = NUM_NANOSECONDS_IN_1_MS; // 1ms -bool SampleProfiler::s_timePeriodIsSet = false; -Volatile SampleProfiler::s_canStartSampling = FALSE; -int32_t SampleProfiler::s_refCount = 0; - -#ifndef TARGET_UNIX -PVOID SampleProfiler::s_timeBeginPeriodFn = NULL; -PVOID SampleProfiler::s_timeEndPeriodFn = NULL; -HINSTANCE SampleProfiler::s_hMultimediaLib = NULL; - -typedef MMRESULT(WINAPI *TimePeriodFnPtr)(UINT uPeriod); -#endif //TARGET_UNIX - -void SampleProfiler::Initialize(EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - if (s_pEventPipeProvider == nullptr) - { - s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName), nullptr, nullptr, pEventPipeProviderCallbackDataQueue); - s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent( - 0, /* eventID */ - 0, /* keywords */ - 0, /* eventVersion */ - EventPipeEventLevel::Informational, - false /* NeedStack */); - } -} - -void SampleProfiler::Enable() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(s_pEventPipeProvider != nullptr); - PRECONDITION(s_pThreadTimeEvent != nullptr); - // Synchronization of multiple callers occurs in EventPipe::Enable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - // Check to see if the sample profiler event is enabled. If it is not, do not spin up the sampling thread. - if (!s_pThreadTimeEvent->IsEnabled()) - { - return; - } - - if (s_canStartSampling) - { - EnableInternal(); - } - - ++s_refCount; -} - -void SampleProfiler::Disable() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - // Synchronization of multiple callers occurs in EventPipe::Disable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - PRECONDITION(s_refCount >= 0); - } - CONTRACTL_END; - - // Bail early if profiling is not enabled. - if (!s_profilingEnabled) - return; - - if (s_refCount == 1) - { - _ASSERTE(!g_fProcessDetach); - - // The sampling thread will watch this value and exit - // when profiling is disabled. - s_profilingEnabled = false; - - // Wait for the sampling thread to clean itself up. - s_threadShutdownEvent.Wait(INFINITE, FALSE /* bAlertable */); - s_threadShutdownEvent.CloseEvent(); - - if (s_timePeriodIsSet) - ResetTimeGranularity(); - UnloadDependencies(); - } - --s_refCount; -} - -void SampleProfiler::CanStartSampling() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - s_canStartSampling = TRUE; - if (s_refCount > 0) - { - EnableInternal(); - } -} - -void SampleProfiler::SetSamplingRate(unsigned long nanoseconds) -{ - LIMITED_METHOD_CONTRACT; - - // If the time period setting was modified by us, - // make sure to change it back before changing our period - // and losing track of what we set it to - if (s_timePeriodIsSet) - { - ResetTimeGranularity(); - } - - s_samplingRateInNs = nanoseconds; - - if (!s_timePeriodIsSet) - { - SetTimeGranularity(); - } -} - -DWORD WINAPI SampleProfiler::ThreadProc(void *args) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(s_pSamplingThread != NULL); - } - CONTRACTL_END; - - // Complete thread initialization and start the profiling loop. - if (s_pSamplingThread->HasStarted()) - { - // Switch to pre-emptive mode so that this thread doesn't starve the GC. - GCX_PREEMP(); - - while (s_profilingEnabled) - { - // Check to see if we can suspend managed execution. - if (ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0)) - { - // Skip the current sample. - PlatformSleep(s_samplingRateInNs); - continue; - } - - // Actually suspend managed execution. - ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_OTHER); - - // Walk all managed threads and capture stacks. - WalkManagedThreads(); - - // Resume managed execution. - ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */); - - // Wait until it's time to sample again. - PlatformSleep(s_samplingRateInNs); - } - } - - // Destroy the sampling thread when it is done running. - DestroyThread(s_pSamplingThread); - s_pSamplingThread = NULL; - - // Signal Disable() that the thread has been destroyed. - s_threadShutdownEvent.Set(); - - return S_OK; -} - -// The thread store lock must already be held by the thread before this function -// is called. ThreadSuspend::SuspendEE acquires the thread store lock. -void SampleProfiler::WalkManagedThreads() -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - Thread *pTargetThread = NULL; - - // Iterate over all managed threads. - // Assumes that the ThreadStoreLock is held because we've suspended all threads. - while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL) - { - StackContents stackContents; - - // Walk the stack and write it out as an event. - if (EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty()) - { - // Set the payload. If the GC mode on suspension > 0, then the thread was in cooperative mode. - // Even though there are some cases where this is not managed code, we assume it is managed code here. - // If the GC mode on suspension == 0 then the thread was in preemptive mode, which we qualify as external here. - - // Write the sample. - EventPipe::WriteSampleProfileEvent( - s_pSamplingThread, - s_pThreadTimeEvent, - pTargetThread, - stackContents, - pTargetThread->GetGCModeOnSuspension() ? s_ManagedPayload.Rawdata : s_ExternalPayload.Rawdata, - c_payloadSize); - } - - // Reset the GC mode. - pTargetThread->ClearGCModeOnSuspension(); - } -} - -void SampleProfiler::PlatformSleep(unsigned long nanoseconds) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - -#ifdef TARGET_UNIX - PAL_nanosleep(nanoseconds); -#else //TARGET_UNIX - ClrSleepEx(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS, FALSE); -#endif //TARGET_UNIX -} - -void SampleProfiler::SetTimeGranularity() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef TARGET_UNIX - // Attempt to set the systems minimum timer period to the sampling rate - // If the sampling rate is lower than the current system setting (16ms by default), - // this will cause the OS to wake more often for scheduling descsion, allowing us to take samples - // Note that is effects a system-wide setting and when set low will increase the amount of time - // the OS is on-CPU, decreasing overall system performance and increasing power consumption - if (s_timeBeginPeriodFn != NULL) - { - if (((TimePeriodFnPtr)s_timeBeginPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR) - { - s_timePeriodIsSet = TRUE; - } - } -#endif //TARGET_UNIX -} - -void SampleProfiler::ResetTimeGranularity() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef TARGET_UNIX - // End the modifications we had to the timer period in Enable - if (s_timeEndPeriodFn != NULL) - { - if (((TimePeriodFnPtr)s_timeEndPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR) - { - s_timePeriodIsSet = FALSE; - } - } -#endif //TARGET_UNIX -} - -bool SampleProfiler::LoadDependencies() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef TARGET_UNIX - if (s_refCount > 0) - return true; // Already loaded. - - s_hMultimediaLib = WszLoadLibrary(W("winmm.dll")); - - if (s_hMultimediaLib != NULL) - { - s_timeBeginPeriodFn = (PVOID)GetProcAddress(s_hMultimediaLib, "timeBeginPeriod"); - s_timeEndPeriodFn = (PVOID)GetProcAddress(s_hMultimediaLib, "timeEndPeriod"); - } - - return s_hMultimediaLib != NULL && s_timeBeginPeriodFn != NULL && s_timeEndPeriodFn != NULL; -#else - return FALSE; -#endif //TARGET_UNIX -} - -void SampleProfiler::UnloadDependencies() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef TARGET_UNIX - if (s_hMultimediaLib != NULL) - { - FreeLibrary(s_hMultimediaLib); - s_hMultimediaLib = NULL; - s_timeBeginPeriodFn = NULL; - s_timeEndPeriodFn = NULL; - } -#endif //TARGET_UNIX -} - -void SampleProfiler::EnableInternal() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(s_pEventPipeProvider != nullptr); - PRECONDITION(s_pThreadTimeEvent != nullptr); - PRECONDITION((!g_fEEStarted && s_refCount > 0) || (s_pSamplingThread == NULL && s_refCount == 0) || ((s_pSamplingThread != NULL && s_refCount > 0))); - // Synchronization of multiple callers occurs in EventPipe::Enable. - PRECONDITION(EventPipe::IsLockOwnedByCurrentThread()); - } - CONTRACTL_END; - - const bool fSuccess = LoadDependencies(); - -#ifndef TARGET_UNIX - _ASSERTE(fSuccess); - // TODO: Stress log on failure? -#else - _ASSERTE(!fSuccess); -#endif - - if (!s_profilingEnabled) - { - s_profilingEnabled = true; - s_pSamplingThread = SetupUnstartedThread(); - if (s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL)) - { - // Start the sampling thread. - s_pSamplingThread->SetBackground(TRUE); - s_pSamplingThread->StartThread(); - } - else - { - _ASSERT(!"Unable to create sample profiler thread."); - } - - s_threadShutdownEvent.CreateManualEvent(FALSE); - - SetTimeGranularity(); - } -} - -#endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/sampleprofiler.h b/src/coreclr/vm/sampleprofiler.h deleted file mode 100644 index 7705623434cb7..0000000000000 --- a/src/coreclr/vm/sampleprofiler.h +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef __SAMPLEPROFILER_H__ -#define __SAMPLEPROFILER_H__ - -#ifdef FEATURE_PERFTRACING - -#include "common.h" -#include "eventpipe.h" - -enum class SampleProfilerSampleType : uint32_t -{ - Error = 0, - External = 1, - Managed = 2 -}; - -class SampleProfiler -{ - // Declare friends. - friend class EventPipe; - -public: - // Initialize the sample profiler. - static void Initialize(EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue); - - // Enable profiling. - static void Enable(); - - // Disable profiling. - static void Disable(); - - // Let the sampler know it's safe to start sampling - static void CanStartSampling(); - - // Set the sampling rate. - static void SetSamplingRate(unsigned long nanoseconds); - - static unsigned long GetSamplingRate() - { - LIMITED_METHOD_CONTRACT; - return s_samplingRateInNs; - } - -private: - union SampleProfilerPayload - { - SampleProfilerSampleType Type; - BYTE Rawdata[sizeof(SampleProfilerSampleType)]; - }; - - // Iterate through all managed threads and walk all stacks. - static void WalkManagedThreads(); - - // Profiling thread proc. Invoked on a new thread when profiling is enabled. - static DWORD WINAPI ThreadProc(void *args); - - // Calls either PAL_nanosleep or ClrSleepEx depending on platform - // Note: Although we specify the time in ns, that is no indication - // of the actually accuracy with which we will return from sleep - // In reality Unix will have a minimum granularity of ~10ms - // and Windows has a default granularity of ~16ms, but can be - // adjusted to as low as ~1ms - // Even this however is not gaurenteed. If the system is under load - // the sampling thread may be delayed up to hundreds of ms due to - // scheduling priority. There is no way to prevent this from user threads - // Additionally we may get lucky and there will be an open CPU to run - // and under light load the timings will achieve great accuracy! - static void PlatformSleep(unsigned long nanoseconds); - - static bool LoadDependencies(); - static void UnloadDependencies(); - - static void EnableInternal(); - -#ifndef TARGET_UNIX - static HINSTANCE s_hMultimediaLib; - static PVOID s_timeBeginPeriodFn; - static PVOID s_timeEndPeriodFn; -#endif //TARGET_UNIX - - static void SetTimeGranularity(); - static void ResetTimeGranularity(); - - // True when profiling is enabled. - static Volatile s_profilingEnabled; - - // The sampling thread. - static Thread *s_pSamplingThread; - - // The provider and event emitted by the profiler. - static const WCHAR *s_providerName; - static EventPipeProvider *s_pEventPipeProvider; - static EventPipeEvent *s_pThreadTimeEvent; - - // Event payloads. - // External represents a sample in external or native code. - // Managed represents a sample in managed code. - static SampleProfilerPayload s_ExternalPayload; - static SampleProfilerPayload s_ManagedPayload; - static const unsigned int c_payloadSize = sizeof(unsigned int); - - // Thread shutdown event for synchronization between Disable() and the sampling thread. - static CLREventStatic s_threadShutdownEvent; - - // The sampling rate. - static unsigned long s_samplingRateInNs; - - // Whether or not timeBeginPeriod has been used to set the scheduler period - static bool s_timePeriodIsSet; - - static Volatile s_canStartSampling; - - static int32_t s_refCount; -}; - -#endif // FEATURE_PERFTRACING - -#endif // __SAMPLEPROFILER_H__ diff --git a/src/native/eventpipe/ds-ipc-posix.c b/src/native/eventpipe/ds-ipc-posix.c index b346a4042c795..38e29c0c5b030 100644 --- a/src/native/eventpipe/ds-ipc-posix.c +++ b/src/native/eventpipe/ds-ipc-posix.c @@ -1,4 +1,7 @@ -#ifdef FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef FEATURE_PERFTRACING_STANDALONE_PAL #define EP_NO_RT_DEPENDENCY #endif @@ -26,7 +29,7 @@ #include #endif // __GNUC__ -#ifndef FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL +#ifndef FEATURE_PERFTRACING_STANDALONE_PAL #include "ds-rt.h" #else #ifdef FEATURE_CORECLR @@ -81,7 +84,7 @@ ep_rt_object_array_free (void *ptr) if (ptr) free (ptr); } -#endif /* !FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL */ +#endif /* !FEATURE_PERFTRACING_STANDALONE_PAL */ #ifdef __APPLE__ #define APPLICATION_CONTAINER_BASE_PATH_SUFFIX "/Library/Group Containers/" @@ -158,7 +161,7 @@ ipc_transport_get_default_name ( ep_char8_t *name, int32_t name_len) { -#ifndef FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL +#ifndef FEATURE_PERFTRACING_STANDALONE_PAL return ds_rt_transport_get_default_name ( name, name_len, @@ -166,7 +169,7 @@ ipc_transport_get_default_name ( ep_rt_current_process_get_id (), NULL, "socket"); -#elif defined (FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL) && defined (FEATURE_CORECLR) +#elif defined (FEATURE_PERFTRACING_STANDALONE_PAL) && defined (FEATURE_CORECLR) // generate the default socket name in TMP Path const ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess(); PAL_GetTransportName( diff --git a/src/native/eventpipe/ds-ipc-win32.c b/src/native/eventpipe/ds-ipc-win32.c index 2a05b36a6bd3a..a1f400f293360 100644 --- a/src/native/eventpipe/ds-ipc-win32.c +++ b/src/native/eventpipe/ds-ipc-win32.c @@ -1,4 +1,7 @@ -#ifdef FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef FEATURE_PERFTRACING_STANDALONE_PAL #define EP_NO_RT_DEPENDENCY #endif @@ -14,7 +17,7 @@ #include #include -#ifndef FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL +#ifndef FEATURE_PERFTRACING_STANDALONE_PAL #include "ds-rt.h" #else #ifndef ep_raise_error_if_nok @@ -52,7 +55,7 @@ ep_rt_object_free (void *ptr) if (ptr) free (ptr); } -#endif /* !FEATURE_PERFTRACING_C_LIB_STANDALONE_PAL */ +#endif /* !FEATURE_PERFTRACING_STANDALONE_PAL */ /* * Forward declares of all static functions. diff --git a/src/native/eventpipe/ep-rt-config.h b/src/native/eventpipe/ep-rt-config.h index c0f032bf76b28..94efb3ebdd6bc 100644 --- a/src/native/eventpipe/ep-rt-config.h +++ b/src/native/eventpipe/ep-rt-config.h @@ -20,7 +20,7 @@ #include "common.h" #endif -#if defined(FEATURE_PERFTRACING) && defined(FEATURE_PERFTRACING_C_LIB) +#if defined(FEATURE_PERFTRACING) #define ENABLE_PERFTRACING #endif