diff options
33 files changed, 1996 insertions, 988 deletions
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs index 1a5de88bd1..9f05540610 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs @@ -196,9 +196,9 @@ namespace System.Diagnostics.Tracing s_sessionID = EventPipeInternal.Enable( configuration.OutputFile, configuration.CircularBufferSizeInMB, - configuration.ProfilerSamplingRateInNanoseconds, + (ulong)configuration.ProfilerSamplingRateInNanoseconds, providers, - providers.Length, + (uint)providers.Length, configuration.MultiFileTraceLengthInSeconds); } @@ -214,7 +214,13 @@ namespace System.Diagnostics.Tracing // These PInvokes are used by the configuration APIs to interact with EventPipe. // [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern UInt64 Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders, ulong multiFileTraceLengthInSeconds); + internal static extern UInt64 Enable( + string outputFile, + uint circularBufferSizeInMB, + ulong profilerSamplingRateInNanoseconds, + EventPipeProviderConfiguration[] providers, + uint numProviders, + ulong multiFileTraceLengthInSeconds); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern void Disable(UInt64 sessionID); diff --git a/src/debug/debug-pal/CMakeLists.txt b/src/debug/debug-pal/CMakeLists.txt index 83e400c28f..c1b5b443a9 100644 --- a/src/debug/debug-pal/CMakeLists.txt +++ b/src/debug/debug-pal/CMakeLists.txt @@ -11,6 +11,7 @@ if(WIN32) include_directories(../../inc) #needed for warning control set(TWO_WAY_PIPE_SOURCES + win/diagnosticsipc.cpp win/twowaypipe.cpp win/processdescriptor.cpp ) @@ -24,6 +25,7 @@ if(CLR_CMAKE_PLATFORM_UNIX) add_definitions(-D_POSIX_C_SOURCE=200809L) set(TWO_WAY_PIPE_SOURCES + unix/diagnosticsipc.cpp unix/twowaypipe.cpp unix/processdescriptor.cpp ) diff --git a/src/debug/debug-pal/unix/diagnosticsipc.cpp b/src/debug/debug-pal/unix/diagnosticsipc.cpp new file mode 100644 index 0000000000..182dff01be --- /dev/null +++ b/src/debug/debug-pal/unix/diagnosticsipc.cpp @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <pal.h> +#include <pal_assert.h> +#include <new> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include "diagnosticsipc.h" +#include "processdescriptor.h" + +IpcStream::DiagnosticsIpc::DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress) : + _serverSocket(serverSocket), + _pServerAddress(new (std::nothrow) sockaddr_un) +{ + _ASSERTE(_pServerAddress != nullptr); + _ASSERTE(_serverSocket != -1); + _ASSERTE(pServerAddress != nullptr); + + if (_pServerAddress == nullptr || pServerAddress == nullptr) + return; + memcpy(_pServerAddress, pServerAddress, sizeof(sockaddr_un)); +} + +IpcStream::DiagnosticsIpc::~DiagnosticsIpc() +{ + if (_serverSocket != -1) + { + const int fSuccessClose = ::close(_serverSocket); + _ASSERTE(fSuccessClose != -1); // TODO: Add error handling? + + const int fSuccessUnlink = ::unlink(_pServerAddress->sun_path); + _ASSERTE(fSuccessUnlink != -1); // TODO: Add error handling? + + delete _pServerAddress; + } +} + +IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback) +{ + const int serverSocket = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (serverSocket == -1) + { + if (callback != nullptr) + callback(strerror(errno), errno); + _ASSERTE(serverSocket != -1); + return nullptr; + } + + sockaddr_un serverAddress{}; + serverAddress.sun_family = AF_UNIX; + const ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess(); + PAL_GetTransportName( + sizeof(serverAddress.sun_path), + serverAddress.sun_path, + pIpcName, + pd.m_Pid, + pd.m_ApplicationGroupId, + "socket"); + + const int fSuccessBind = ::bind(serverSocket, (sockaddr *)&serverAddress, sizeof(serverAddress)); + if (fSuccessBind == -1) + { + if (callback != nullptr) + callback(strerror(errno), errno); + _ASSERTE(fSuccessBind != -1); + return nullptr; + } + + return new IpcStream::DiagnosticsIpc(serverSocket, &serverAddress); +} + +IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const +{ + if (::listen(_serverSocket, /* backlog */ 255) == -1) + return nullptr; + sockaddr_un from; + socklen_t fromlen = sizeof(from); + const int clientSocket = ::accept(_serverSocket, (sockaddr *)&from, &fromlen); + if (clientSocket == -1) + { + if (callback != nullptr) + callback(strerror(errno), errno); + return nullptr; + } + + auto pIpcStream = new (std::nothrow) IpcStream(clientSocket); + if (pIpcStream == nullptr && callback != nullptr) + callback("Failed to allocate an IpcStream object.", 1); + return pIpcStream; +} + +IpcStream::~IpcStream() +{ + if (_clientSocket != -1) + { + const int fSuccessClose = ::close(_clientSocket); + _ASSERTE(fSuccessClose != -1); + } +} + +bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const +{ + _ASSERTE(lpBuffer != nullptr); + + const ssize_t ssize = ::recv(_clientSocket, lpBuffer, nBytesToRead, 0); + const bool fSuccess = ssize != -1; + + if (!fSuccess) + { + // TODO: Add error handling. + } + + nBytesRead = static_cast<uint32_t>(ssize); + return fSuccess; +} + +bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const +{ + _ASSERTE(lpBuffer != nullptr); + + const ssize_t ssize = ::send(_clientSocket, lpBuffer, nBytesToWrite, 0); + const bool fSuccess = ssize != -1; + + if (!fSuccess) + { + // TODO: Add error handling. + } + + nBytesWritten = static_cast<uint32_t>(ssize); + return fSuccess; +} + +bool IpcStream::Flush() const +{ + // fsync - http://man7.org/linux/man-pages/man2/fsync.2.html ??? + return true; +} diff --git a/src/debug/debug-pal/win/diagnosticsipc.cpp b/src/debug/debug-pal/win/diagnosticsipc.cpp new file mode 100644 index 0000000000..7d9f84e7e5 --- /dev/null +++ b/src/debug/debug-pal/win/diagnosticsipc.cpp @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <assert.h> +#include <new> +#include <stdio.h> +#include "diagnosticsipc.h" + +IpcStream::DiagnosticsIpc::DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength]) +{ + memcpy(_pNamedPipeName, namedPipeName, sizeof(_pNamedPipeName)); +} + +IpcStream::DiagnosticsIpc::~DiagnosticsIpc() +{ +} + +IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback) +{ + assert(pIpcName != nullptr); + if (pIpcName == nullptr) + return nullptr; + + char namedPipeName[MaxNamedPipeNameLength]{}; + const int nCharactersWritten = sprintf_s( + namedPipeName, + sizeof(namedPipeName), + "\\\\.\\pipe\\%s-%d", + pIpcName, + ::GetCurrentProcessId()); + + if (nCharactersWritten == -1) + { + if (callback != nullptr) + callback("Failed to generate the named pipe name", nCharactersWritten); + assert(nCharactersWritten != -1); + return nullptr; + } + + return new IpcStream::DiagnosticsIpc(namedPipeName); +} + +IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const +{ + const uint32_t nInBufferSize = 16 * 1024; + const uint32_t nOutBufferSize = 16 * 1024; + HANDLE hPipe = ::CreateNamedPipeA( + _pNamedPipeName, // pipe name + PIPE_ACCESS_DUPLEX/* | FILE_FLAG_OVERLAPPED*/, // read/write access + PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, // message type pipe, message-read and blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + nOutBufferSize, // output buffer size + nInBufferSize, // input buffer size + 0, // default client time-out + NULL); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) + { + if (callback != nullptr) + callback("Failed to create an instance of a named pipe.", ::GetLastError()); + return nullptr; + } + + const BOOL fSuccess = ::ConnectNamedPipe(hPipe, NULL) != 0; + const DWORD errorCode = ::GetLastError(); + if (!fSuccess && (errorCode != ERROR_PIPE_CONNECTED)) + { + if (callback != nullptr) + callback("Failed to wait for a client process to connect.", errorCode); + return nullptr; + } + + auto pIpcStream = new (std::nothrow) IpcStream(hPipe); + if (pIpcStream == nullptr && callback != nullptr) + callback("Failed to allocate an IpcStream object.", 1); + return pIpcStream; +} + +IpcStream::~IpcStream() +{ + if (_hPipe != INVALID_HANDLE_VALUE) + { + const BOOL fSuccessDisconnectNamedPipe = ::DisconnectNamedPipe(_hPipe); + assert(fSuccessDisconnectNamedPipe != 0); + + const BOOL fSuccessCloseHandle = ::CloseHandle(_hPipe); + assert(CloseHandle != 0); + } +} + +bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const +{ + assert(lpBuffer != nullptr); + + DWORD nNumberOfBytesRead = 0; + const bool fSuccess = ::ReadFile( + _hPipe, // handle to pipe + lpBuffer, // buffer to receive data + nBytesToRead, // size of buffer + &nNumberOfBytesRead, // number of bytes read + NULL) != 0; // not overlapped I/O + + if (!fSuccess) + { + // TODO: Add error handling. + } + + nBytesRead = static_cast<uint32_t>(nNumberOfBytesRead); + return fSuccess; +} + +bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const +{ + assert(lpBuffer != nullptr); + + DWORD nNumberOfBytesWritten = 0; + const bool fSuccess = ::WriteFile( + _hPipe, // handle to pipe + lpBuffer, // buffer to write from + nBytesToWrite, // number of bytes to write + &nNumberOfBytesWritten, // number of bytes written + NULL) != 0; // not overlapped I/O + + if (!fSuccess) + { + // TODO: Add error handling. + } + + nBytesWritten = static_cast<uint32_t>(nNumberOfBytesWritten); + return fSuccess; +} + +bool IpcStream::Flush() const +{ + const bool fSuccess = ::FlushFileBuffers(_hPipe) != 0; + if (!fSuccess) + { + // TODO: Add error handling. + } + return fSuccess; +} diff --git a/src/debug/inc/diagnosticsipc.h b/src/debug/inc/diagnosticsipc.h new file mode 100644 index 0000000000..81789f0803 --- /dev/null +++ b/src/debug/inc/diagnosticsipc.h @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __DIAGNOSTICS_IPC_H__ +#define __DIAGNOSTICS_IPC_H__ + +#include <stdint.h> + +#ifdef FEATURE_PAL + struct sockaddr_un; +#else + #include <Windows.h> +#endif /* FEATURE_PAL */ + +typedef void (*ErrorCallback)(const char *szMessage, uint32_t code); + +class IpcStream final +{ +public: + ~IpcStream(); + bool Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const; + bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const; + bool Flush() const; + + class DiagnosticsIpc final + { + public: + static DiagnosticsIpc *Create(const char *const pIpcName, ErrorCallback callback = nullptr); + ~DiagnosticsIpc(); + IpcStream *Accept(ErrorCallback callback = nullptr) const; + + private: + +#ifdef FEATURE_PAL + DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress); + const int _serverSocket = -1; + sockaddr_un *const _pServerAddress; +#else + static const uint32_t MaxNamedPipeNameLength = 256; + DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength]); + char _pNamedPipeName[MaxNamedPipeNameLength]; // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createnamedpipea +#endif /* FEATURE_PAL */ + + DiagnosticsIpc() = delete; + DiagnosticsIpc(const DiagnosticsIpc &src) = delete; + DiagnosticsIpc(DiagnosticsIpc &&src) = delete; + DiagnosticsIpc &operator=(const DiagnosticsIpc &rhs) = delete; + DiagnosticsIpc &&operator=(DiagnosticsIpc &&rhs) = delete; + }; + +private: +#ifdef FEATURE_PAL + int _clientSocket = -1; + IpcStream(int clientSocket) : _clientSocket(clientSocket) {} +#else + HANDLE _hPipe = INVALID_HANDLE_VALUE; + IpcStream(HANDLE hPipe) : _hPipe(hPipe) {} +#endif /* FEATURE_PAL */ + + IpcStream() = delete; + IpcStream(const IpcStream &src) = delete; + IpcStream(IpcStream &&src) = delete; + IpcStream &operator=(const IpcStream &rhs) = delete; + IpcStream &&operator=(IpcStream &&rhs) = delete; +}; + +#endif // __DIAGNOSTICS_IPC_H__ diff --git a/src/inc/stresslog.h b/src/inc/stresslog.h index dd8c029b0a..8f98f9a7b2 100644 --- a/src/inc/stresslog.h +++ b/src/inc/stresslog.h @@ -127,7 +127,7 @@ LOG((facility, level, msg, data1, data2, data3, data4, data5, data6, data7)); \ } while(0) -#define STRESS_LOG_COND0(facility, level, msg) do { \ +#define STRESS_LOG_COND0(facility, level, cond, msg) do { \ if (StressLog::LogOn(facility, level) && (cond)) \ StressLog::LogMsg(level, facility, 0, msg); \ LOG((facility, level, msg)); \ diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 26c9e3f6d8..1be000ce8a 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -480,6 +480,17 @@ static const int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH; PALIMPORT VOID PALAPI +PAL_GetTransportName( + const int MAX_TRANSPORT_NAME_LENGTH, + OUT char *name, + IN const char *prefix, + IN DWORD id, + IN const char *applicationGroupId, + IN const char *suffix); + +PALIMPORT +VOID +PALAPI PAL_GetTransportPipeName( OUT char *name, IN DWORD id, diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 5615036806..f7618cbdb9 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -1549,7 +1549,8 @@ static uint64_t HashSemaphoreName(uint64_t a, uint64_t b) #define HashSemaphoreName(a,b) a,b #endif -static const char* PipeNameFormat = "clr-debug-pipe-%d-%llu-%s"; +static const char *const TwoWayNamedPipePrefix = "clr-debug-pipe"; +static const char* IpcNameFormat = "%s-%d-%llu-%s"; class PAL_RuntimeStartupHelper { @@ -2288,14 +2289,16 @@ GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey) /*++ Function: - PAL_GetTransportPipeName + PAL_GetTransportName - Builds the transport pipe names from the process id. + Builds the transport IPC names from the process id. --*/ VOID PALAPI -PAL_GetTransportPipeName( +PAL_GetTransportName( + const int MAX_TRANSPORT_NAME_LENGTH, OUT char *name, + IN const char *prefix, IN DWORD id, IN const char *applicationGroupId, IN const char *suffix) @@ -2305,7 +2308,7 @@ PAL_GetTransportPipeName( UINT64 disambiguationKey = 0; PathCharString formatBufferString; BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey); - char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH-1); + char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_TRANSPORT_NAME_LENGTH-1); if (formatBuffer == nullptr) { ERROR("Out Of Memory"); @@ -2337,9 +2340,9 @@ PAL_GetTransportPipeName( } // Verify the size of the path won't exceed maximum allowed size - if (formatBufferString.GetCount() >= MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH) + if (formatBufferString.GetCount() >= MAX_TRANSPORT_NAME_LENGTH) { - ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); + ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH"); return; } } @@ -2347,27 +2350,50 @@ PAL_GetTransportPipeName( #endif // __APPLE__ { // Get a temp file location - dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer); + dwRetVal = ::GetTempPathA(MAX_TRANSPORT_NAME_LENGTH, formatBuffer); if (dwRetVal == 0) { ERROR("GetTempPath failed (0x%08x)", ::GetLastError()); return; } - if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH) + if (dwRetVal > MAX_TRANSPORT_NAME_LENGTH) { - ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); + ERROR("GetTempPath returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH"); return; } } - if (strncat_s(formatBuffer, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE) + if (strncat_s(formatBuffer, MAX_TRANSPORT_NAME_LENGTH, IpcNameFormat, strlen(IpcNameFormat)) == STRUNCATE) { - ERROR("TransportPipeName was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); + ERROR("TransportPipeName was larger than MAX_TRANSPORT_NAME_LENGTH"); return; } - int chars = snprintf(name, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer, id, disambiguationKey, suffix); - _ASSERTE(chars > 0 && chars < MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH); + int chars = snprintf(name, MAX_TRANSPORT_NAME_LENGTH, formatBuffer, prefix, id, disambiguationKey, suffix); + _ASSERTE(chars > 0 && chars < MAX_TRANSPORT_NAME_LENGTH); +} + +/*++ + Function: + PAL_GetTransportPipeName + + Builds the transport pipe names from the process id. +--*/ +VOID +PALAPI +PAL_GetTransportPipeName( + OUT char *name, + IN DWORD id, + IN const char *applicationGroupId, + IN const char *suffix) +{ + PAL_GetTransportName( + MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, + name, + TwoWayNamedPipePrefix, + id, + applicationGroupId, + suffix); } /*++ diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 397f4e2ea5..b7ae17a156 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -306,6 +306,7 @@ set(VM_SOURCES_WKS comwaithandle.cpp customattribute.cpp custommarshalerinfo.cpp + diagnosticserver.cpp dllimportcallback.cpp eeconfig.cpp eecontract.cpp @@ -319,12 +320,15 @@ set(VM_SOURCES_WKS eventpipeeventsource.cpp eventpipeblock.cpp eventpipefile.cpp + eventpipeinternal.cpp eventpipejsonfile.cpp eventpipemetadatagenerator.cpp + eventpipeprotocolhelper.cpp eventpipeprovider.cpp eventpipebuffer.cpp eventpipebuffermanager.cpp eventpipesession.cpp + eventpipesessionprovider.cpp eventstore.cpp fastserializer.cpp fcall.cpp @@ -421,6 +425,8 @@ set(VM_HEADERS_WKS comwaithandle.h customattribute.h custommarshalerinfo.h + diagnosticserver.h + diagnosticsprotocol.h dllimportcallback.h eeconfig.h eecontract.h @@ -439,10 +445,13 @@ set(VM_HEADERS_WKS eventpipeeventinstance.h eventpipeeventsource.h eventpipefile.h + eventpipeinternal.h eventpipejsonfile.h eventpipemetadatagenerator.h + eventpipeprotocolhelper.h eventpipeprovider.h eventpipesession.h + eventpipesessionprovider.h eventstore.hpp fastserializer.h fcall.h diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp index e5267cf131..1b85649d67 100644 --- a/src/vm/ceemain.cpp +++ b/src/vm/ceemain.cpp @@ -215,6 +215,7 @@ #include "perfmap.h" #endif +#include "diagnosticserver.h" #include "eventpipe.h" #ifndef FEATURE_PAL @@ -667,6 +668,7 @@ void EEStartupHelper(COINITIEE fFlags) #ifdef FEATURE_PERFTRACING // Initialize the event pipe. + DiagnosticServer::Initialize(); EventPipe::Initialize(); #endif // FEATURE_PERFTRACING @@ -1462,6 +1464,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) #ifdef FEATURE_PERFTRACING // Shutdown the event pipe. EventPipe::Shutdown(); + DiagnosticServer::Shutdown(); #endif // FEATURE_PERFTRACING #if defined(FEATURE_COMINTEROP) diff --git a/src/vm/common.h b/src/vm/common.h index 9050ae7c63..2a91e77220 100644 --- a/src/vm/common.h +++ b/src/vm/common.h @@ -75,7 +75,6 @@ #include <stdlib.h> #include <wchar.h> #include <objbase.h> -#include <stddef.h> #include <float.h> #include <math.h> #include <time.h> diff --git a/src/vm/diagnosticserver.cpp b/src/vm/diagnosticserver.cpp new file mode 100644 index 0000000000..c0e11d100f --- /dev/null +++ b/src/vm/diagnosticserver.cpp @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "diagnosticserver.h" +#include "diagnosticsipc.h" +#include "eventpipeprotocolhelper.h" + +#ifdef FEATURE_PAL +#include "pal.h" +#endif // FEATURE_PAL + +#ifdef FEATURE_PERFTRACING + +static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter) +{ + CONTRACTL + { + // TODO: Maybe this should not throw. + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(lpThreadParameter != nullptr); + } + CONTRACTL_END; + + auto pIpc = reinterpret_cast<IpcStream::DiagnosticsIpc *>(lpThreadParameter); + if (pIpc == nullptr) + { + STRESS_LOG0(LF_STARTUP, LL_ERROR,"Diagnostics IPC listener was undefined\n"); + return 1; + } + +#ifdef _DEBUG + ErrorCallback LoggingCallback = [](const char *szMessage, uint32_t code) { + LOG((LF_REMOTING, LL_WARNING, "warning (%d): %s.\n", code, szMessage)); + }; +#else + ErrorCallback LoggingCallback = nullptr; +#endif + + while (true) + { + // FIXME: Ideally this would be something like a std::shared_ptr + IpcStream *pStream = pIpc->Accept(LoggingCallback); + if (pStream == nullptr) + continue; + + // TODO: Read operation should happen in a loop. + uint32_t nNumberOfBytesRead = 0; + MessageHeader header; + bool fSuccess = pStream->Read(&header, sizeof(header), nNumberOfBytesRead); + if (!fSuccess || nNumberOfBytesRead != sizeof(header)) + { + delete pStream; + continue; + } + + // TODO: Dispatch thread worker. + switch (header.RequestType) + { + case DiagnosticMessageType::EnableEventPipe: + EventPipeProtocolHelper::EnableFileTracingEventHandler(pStream); + break; + + case DiagnosticMessageType::DisableEventPipe: + EventPipeProtocolHelper::DisableTracingEventHandler(pStream); + break; + + default: + LOG((LF_REMOTING, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType)); + break; + } + } + + return 0; +} + +bool DiagnosticServer::Initialize() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + bool fSuccess = false; + + EX_TRY + { + auto ErrorCallback = [](const char *szMessage, uint32_t code) { + STRESS_LOG2( + LF_STARTUP, // facility + LL_ERROR, // level + "Failed to create diagnostic IPC: error (%d): %s.\n", // msg + code, // data1 + szMessage); // data2 + }; + IpcStream::DiagnosticsIpc *pIpc = IpcStream::DiagnosticsIpc::Create( + "dotnetcore-diagnostic", ErrorCallback); + + if (pIpc != nullptr) + { + DWORD dwThreadId = 0; + HANDLE hThread = ::CreateThread( // TODO: Is it correct to have this "lower" level call here? + nullptr, // no security attribute + 0, // default stack size + DiagnosticsServerThread, // thread proc + (LPVOID)pIpc, // thread parameter + 0, // not suspended + &dwThreadId); // returns thread ID + + if (hThread == nullptr) + { + // Failed to create IPC thread. + STRESS_LOG1( + LF_STARTUP, // facility + LL_ERROR, // level + "Failed to create diagnostic server thread (%d).\n", // msg + ::GetLastError()); // data1 + } + else + { + // FIXME: Maybe hold on to the thread to abort/cleanup at exit? + ::CloseHandle(hThread); + + // TODO: Add error handling? + 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; + + EX_TRY + { + // FIXME: Stop IPC server thread? + fSuccess = true; + } + EX_CATCH + { + fSuccess = false; + // TODO: Should we log anything here? + } + EX_END_CATCH(SwallowAllExceptions); + + return fSuccess; +} + +#endif // FEATURE_PERFTRACING diff --git a/src/vm/diagnosticserver.h b/src/vm/diagnosticserver.h new file mode 100644 index 0000000000..95399a2ef8 --- /dev/null +++ b/src/vm/diagnosticserver.h @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __DIAGNOSTIC_SERVER_H__ +#define __DIAGNOSTIC_SERVER_H__ + +#include <stdint.h> + +#ifdef FEATURE_PERFTRACING // This macro should change to something more generic than performance. + +//! TODO: Temp class. +enum class DiagnosticMessageType : uint32_t +{ + /////////////////////////////////////////////////////////////////////////// + // Debug = 0 + + /////////////////////////////////////////////////////////////////////////// + // EventPipe + EnableEventPipe = 1024, + DisableEventPipe, + + // TODO: Define what else is available on the out-of-proc interface? + // GetSessionInfo, + // CreateProvider, + // DefineEvent, + // GetProvider, + // DeleteProvider, + // EventActivityIdControl, + // WriteEvent, + // WriteEventData, + // GetNextEvent, + + /////////////////////////////////////////////////////////////////////////// + // Profiler = 2048 +}; + +//! TODO: Temp class. +struct MessageHeader +{ + DiagnosticMessageType RequestType; + uint32_t Pid; +}; + +//! 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(); +}; + +#endif // FEATURE_PERFTRACING + +#endif // __DIAGNOSTIC_SERVER_H__ diff --git a/src/vm/diagnosticsprotocol.h b/src/vm/diagnosticsprotocol.h new file mode 100644 index 0000000000..765977f02a --- /dev/null +++ b/src/vm/diagnosticsprotocol.h @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __DIAGNOSTICS_PROTOCOL_H__ +#define __DIAGNOSTICS_PROTOCOL_H__ + +#ifdef FEATURE_PERFTRACING + +template <typename T> +bool TryParse(uint8_t *&bufferCursor, uint32_t &bufferLen, T &result) +{ + static_assert( + std::is_integral<T>::value || std::is_same<T, float>::value || std::is_same<T, double>::value, + "Can only be instantiated with integral and floating point types."); + + if (bufferLen < sizeof(T)) + return false; + result = *(reinterpret_cast<T *>(bufferCursor)); + bufferCursor += sizeof(T); + bufferLen -= sizeof(T); + return true; +} + +template <typename T> +bool TryParseString(uint8_t *&bufferCursor, uint32_t &bufferLen, const T *&result) +{ + static_assert( + std::is_same<T, char>::value || std::is_same<T, wchar_t>::value, + "Can only be instantiated with char and wchar_t 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<const T *>(bufferCursor))[stringLen - 1] != 0) + return false; + result = reinterpret_cast<const T *>(bufferCursor); + + const uint32_t TotalStringLength = stringLen * sizeof(T); + bufferCursor += TotalStringLength; + bufferLen -= TotalStringLength; + return true; +} + +#endif // FEATURE_PERFTRACING + +#endif // __DIAGNOSTICS_PROTOCOL_H__ diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index 4de0b3c943..634f1d0d69 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -8,6 +8,7 @@ #include "eventpipe.h" #include "eventpipebuffermanager.h" #include "eventpipeconfiguration.h" +#include "eventpipesessionprovider.h" #include "eventpipeevent.h" #include "eventpipeeventsource.h" #include "eventpipefile.h" @@ -26,12 +27,12 @@ CrstStatic EventPipe::s_configCrst; bool EventPipe::s_tracingInitialized = false; -EventPipeConfiguration* EventPipe::s_pConfig = NULL; -EventPipeSession* EventPipe::s_pSession = NULL; -EventPipeBufferManager* EventPipe::s_pBufferManager = NULL; +EventPipeConfiguration *EventPipe::s_pConfig = NULL; +EventPipeSession *EventPipe::s_pSession = NULL; +EventPipeBufferManager *EventPipe::s_pBufferManager = NULL; LPCWSTR EventPipe::s_pOutputPath = NULL; -EventPipeFile* EventPipe::s_pFile = NULL; -EventPipeEventSource* EventPipe::s_pEventSource = NULL; +EventPipeFile *EventPipe::s_pFile = NULL; +EventPipeEventSource *EventPipe::s_pEventSource = NULL; LPCWSTR EventPipe::s_pCommandLine = NULL; unsigned long EventPipe::s_nextFileIndex; HANDLE EventPipe::s_fileSwitchTimerHandle = NULL; @@ -78,7 +79,7 @@ EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int m_allocatedData = false; S_UINT32 tmp_size = S_UINT32(0); - for (unsigned int i=0; i<m_eventDataCount; i++) + for (unsigned int i = 0; i < m_eventDataCount; i++) { tmp_size += S_UINT32(m_pEventData[i].Size); } @@ -106,7 +107,7 @@ EventPipeEventPayload::~EventPipeEventPayload() } CONTRACTL_END; - if(m_allocatedData && m_pData != NULL) + if (m_allocatedData && m_pData != NULL) { delete[] m_pData; m_pData = NULL; @@ -123,11 +124,11 @@ void EventPipeEventPayload::Flatten() } CONTRACTL_END; - if(m_size > 0) + if (m_size > 0) { if (!IsFlattened()) { - BYTE* tmp_pData = new (nothrow) BYTE[m_size]; + BYTE *tmp_pData = new (nothrow) BYTE[m_size]; if (tmp_pData != NULL) { m_allocatedData = true; @@ -148,26 +149,26 @@ void EventPipeEventPayload::CopyData(BYTE *pDst) } CONTRACTL_END; - if(m_size > 0) + if (m_size > 0) { - if(IsFlattened()) + if (IsFlattened()) { memcpy(pDst, m_pData, m_size); } - else if(m_pEventData != NULL) + else if (m_pEventData != NULL) { unsigned int offset = 0; - for(unsigned int i=0; i<m_eventDataCount; i++) + for (unsigned int i = 0; i < m_eventDataCount; i++) { - memcpy(pDst + offset, (BYTE*) m_pEventData[i].Ptr, m_pEventData[i].Size); + memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size); offset += m_pEventData[i].Size; } } } } -BYTE* EventPipeEventPayload::GetFlatData() +BYTE *EventPipeEventPayload::GetFlatData() { CONTRACTL { @@ -222,7 +223,7 @@ void EventPipe::Shutdown() { Disable((EventPipeSessionID)s_pSession); } - EX_CATCH { } + EX_CATCH {} EX_END_CATCH(SwallowAllExceptions); // Save pointers to the configuration and buffer manager. @@ -236,42 +237,48 @@ void EventPipe::Shutdown() FlushProcessWriteBuffers(); // Free resources. - delete(pConfig); - delete(pBufferManager); - delete(s_pEventSource); + delete pConfig; + delete pBufferManager; + delete s_pEventSource; s_pEventSource = NULL; - delete(s_pOutputPath); + delete[] s_pOutputPath; s_pOutputPath = NULL; // On Windows, this is just a pointer to the return value from // GetCommandLineW(), so don't attempt to free it. #ifdef FEATURE_PAL - delete[](s_pCommandLine); + delete[] s_pCommandLine; s_pCommandLine = NULL; #endif } EventPipeSessionID EventPipe::Enable( LPCWSTR strOutputPath, - unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - int numProviders, - UINT64 multiFileTraceLengthInSeconds) + uint32_t circularBufferSizeInMB, + uint64_t profilerSamplingRateInNanoseconds, + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; + PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr)); } CONTRACTL_END; + // Take the lock before enabling tracing. + CrstHolder _crst(GetLock()); + // Create a new session. + SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds); EventPipeSession *pSession = s_pConfig->CreateSession( (strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming, circularBufferSizeInMB, pProviders, - static_cast<unsigned int>(numProviders), + numProviders, multiFileTraceLengthInSeconds); // Enable the session. @@ -286,27 +293,21 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS GC_TRIGGERS; MODE_ANY; PRECONDITION(pSession != NULL); + PRECONDITION(GetLock()->OwnedByCurrentThread()); } CONTRACTL_END; // If tracing is not initialized or is already enabled, bail here. - if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) - { + if (!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) return 0; - } // If the state or arguments are invalid, bail here. - if(pSession == NULL || !pSession->IsValid()) - { + if (pSession == NULL || !pSession->IsValid()) return 0; - } // Enable the EventPipe EventSource. s_pEventSource->Enable(pSession); - // Take the lock before enabling tracing. - CrstHolder _crst(GetLock()); - // Initialize the next file index. s_nextFileIndex = 1; @@ -342,7 +343,7 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS SampleProfiler::Enable(); // Enable the file switch timer if needed. - if(s_pSession->GetMultiFileTraceLengthInSeconds() > 0) + if (s_pSession->GetMultiFileTraceLengthInSeconds() > 0) { CreateFileSwitchTimer(); } @@ -363,7 +364,7 @@ void EventPipe::Disable(EventPipeSessionID id) // Only perform the disable operation if the session ID // matches the current active session. - if(id != (EventPipeSessionID)s_pSession) + if (id != (EventPipeSessionID)s_pSession) { return; } @@ -374,7 +375,7 @@ void EventPipe::Disable(EventPipeSessionID id) // Take the lock before disabling tracing. CrstHolder _crst(GetLock()); - if(s_pConfig != NULL && s_pConfig->Enabled()) + if (s_pConfig != NULL && s_pConfig->Enabled()) { // Disable the profiler. SampleProfiler::Disable(); @@ -399,30 +400,31 @@ void EventPipe::Disable(EventPipeSessionID id) FlushProcessWriteBuffers(); // Write to the file. - if(s_pFile != NULL) + if (s_pFile != NULL) { LARGE_INTEGER disableTimeStamp; QueryPerformanceCounter(&disableTimeStamp); s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp); - if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0) { // Before closing the file, do rundown. - const unsigned int numRundownProviders = 2; - EventPipeProviderConfiguration rundownProviders[] = - { - { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL }, // Public provider. - { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL } // Rundown provider. + const EventPipeProviderConfiguration RundownProviders[] = { + {W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL}, // Public provider. + {W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider. }; + // The circular buffer size doesn't matter because all events are written synchronously during rundown. - s_pSession = s_pConfig->CreateSession(EventPipeSessionType::File, 1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders); + s_pSession = s_pConfig->CreateSession( + EventPipeSessionType::File, + 1 /* circularBufferSizeInMB */, + RundownProviders, + sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration)); s_pConfig->EnableRundown(s_pSession); // Ask the runtime to emit rundown events. - if(g_fEEStarted && !g_fEEShutDown) - { + if (g_fEEStarted && !g_fEEShutDown) ETW::EnumerationLog::EndRundown(); - } // Disable the event pipe now that rundown is complete. s_pConfig->Disable(s_pSession); @@ -432,7 +434,7 @@ void EventPipe::Disable(EventPipeSessionID id) s_pSession = NULL; } - delete(s_pFile); + delete s_pFile; s_pFile = NULL; } @@ -456,7 +458,7 @@ void EventPipe::CreateFileSwitchTimer() } CONTRACTL_END - NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext(); + NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext(); if (timerContextHolder == NULL) { return; @@ -503,7 +505,7 @@ void EventPipe::DeleteFileSwitchTimer() } CONTRACTL_END - if((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL))) + if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL))) { s_fileSwitchTimerHandle = NULL; } @@ -520,25 +522,23 @@ void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN ti } CONTRACTL_END; - // Take the lock control lock to make sure that tracing isn't disabled during this operation. CrstHolder _crst(GetLock()); // Make sure that we should actually switch files. UINT64 multiFileTraceLengthInSeconds = s_pSession->GetMultiFileTraceLengthInSeconds(); - if(!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0) + if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0) { return; } GCX_PREEMP(); - if(CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000))) + if (CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000))) { SwitchToNextFile(); s_lastFileSwitchTime = CLRGetTickCount64(); } - } void EventPipe::SwitchToNextFile() @@ -562,14 +562,15 @@ void EventPipe::SwitchToNextFile() // Open the new file. SString nextTraceFilePath; GetNextFilePath(s_pSession, nextTraceFilePath); - EventPipeFile* pFile = new (nothrow) EventPipeFile(nextTraceFilePath); - if(pFile == NULL) + EventPipeFile *pFile = new (nothrow) EventPipeFile(nextTraceFilePath); + if (pFile == NULL) { + // TODO: Add error handling. return; } // Close the previous file. - delete(s_pFile); + delete s_pFile; // Swap in the new file. s_pFile = pFile; @@ -592,11 +593,11 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi // If multiple files have been requested, then add a sequence number to the trace file name. UINT64 multiFileTraceLengthInSeconds = pSession->GetMultiFileTraceLengthInSeconds(); - if(multiFileTraceLengthInSeconds > 0) + if (multiFileTraceLengthInSeconds > 0) { // Remove the ".netperf" file extension if it exists. SString::Iterator netPerfExtension = nextTraceFilePath.End(); - if(nextTraceFilePath.FindBack(netPerfExtension, W(".netperf"))) + if (nextTraceFilePath.FindBack(netPerfExtension, W(".netperf"))) { nextTraceFilePath.Truncate(netPerfExtension); } @@ -608,12 +609,12 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi } } -EventPipeSession* EventPipe::GetSession(EventPipeSessionID id) +EventPipeSession *EventPipe::GetSession(EventPipeSessionID id) { LIMITED_METHOD_CONTRACT; EventPipeSession *pSession = NULL; - if((EventPipeSessionID)s_pSession == id) + if ((EventPipeSessionID)s_pSession == id) { pSession = s_pSession; } @@ -625,7 +626,7 @@ bool EventPipe::Enabled() LIMITED_METHOD_CONTRACT; bool enabled = false; - if(s_pConfig != NULL) + if (s_pConfig != NULL) { enabled = s_pConfig->Enabled(); } @@ -633,7 +634,7 @@ bool EventPipe::Enabled() return enabled; } -EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) +EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) { CONTRACTL { @@ -650,10 +651,9 @@ EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventP } return pProvider; - } -EventPipeProvider* EventPipe::GetProvider(const SString &providerName) +EventPipeProvider *EventPipe::GetProvider(const SString &providerName) { CONTRACTL { @@ -687,9 +687,9 @@ void EventPipe::DeleteProvider(EventPipeProvider *pProvider) // where we hold a provider after tracing has been disabled. CrstHolder _crst(GetLock()); - if(pProvider != NULL) + if (pProvider != NULL) { - if(Enabled()) + if (Enabled()) { // Save the provider until the end of the tracing session. pProvider->SetDeleteDeferred(); @@ -744,7 +744,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload CONTRACTL_END; // Exit early if the event is not enabled. - if(!event.IsEnabled()) + if (!event.IsEnabled()) { return; } @@ -752,7 +752,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload // Get the current thread; Thread *pThread = GetThread(); - if(s_pConfig == NULL) + if (s_pConfig == NULL) { // We can't procede without a configuration return; @@ -760,23 +760,23 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload _ASSERTE(s_pSession != NULL); // 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 == NULL && pThread != NULL) + // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL + if (pActivityId == NULL && pThread != NULL) { pActivityId = pThread->GetActivityId(); } - if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL) + if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL) { s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId); } - else if(s_pConfig->RundownEnabled()) + else if (s_pConfig->RundownEnabled()) { // It is possible that some events that are enabled on rundown can be emitted from other threads. // We're not interested in these events and they can cause corrupted trace files because rundown // events are written synchronously and not under lock. // If we encounter an event that did not originate on the thread that is doing rundown, ignore it. - if(pThread == NULL || !s_pConfig->IsRundownThread(pThread)) + if (pThread == NULL || !s_pConfig->IsRundownThread(pThread)) { return; } @@ -800,7 +800,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload pRelatedActivityId); instance.EnsureStack(*s_pSession); - if(s_pFile != NULL) + if (s_pFile != NULL) { // EventPipeFile::WriteEvent needs to allocate a metadata event // and can therefore throw. In this context we will silently @@ -809,7 +809,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload { s_pFile->WriteEvent(instance); } - EX_CATCH { } + EX_CATCH {} EX_END_CATCH(SwallowAllExceptions); } } @@ -829,7 +829,7 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent EventPipeEventPayload payload(pData, length); // Write the event to the thread's buffer. - if(s_pBufferManager != NULL) + if (s_pBufferManager != NULL) { // Specify the sampling thread as the "current thread", so that we select the right buffer. // Specify the target thread so that the event gets properly attributed. @@ -848,7 +848,7 @@ bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents) CONTRACTL_END; Thread *pThread = GetThread(); - if(pThread != NULL) + if (pThread != NULL) { return WalkManagedStackForThread(pThread, stackContents); } @@ -869,12 +869,12 @@ bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackC // Calling into StackWalkFrames in preemptive mode violates the host contract, // but this contract is not used on CoreCLR. - CONTRACT_VIOLATION( HostViolation ); + CONTRACT_VIOLATION(HostViolation); stackContents.Reset(); StackWalkAction swaRet = pThread->StackWalkFrames( - (PSTACKWALKFRAMESCALLBACK) &StackWalkCallback, + (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback, &stackContents, ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS); @@ -895,9 +895,9 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa // Get the IP. UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC; - if(controlPC == 0) + if (controlPC == 0) { - if(pData->GetLength() == 0) + if (pData->GetLength() == 0) { // This happens for pinvoke stubs on the top of the stack. return SWA_CONTINUE; @@ -909,21 +909,20 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa // Add the IP to the captured stack. pData->Append( controlPC, - pCf->GetFunction() - ); + pCf->GetFunction()); // Continue the stack walk. return SWA_CONTINUE; } -EventPipeConfiguration* EventPipe::GetConfiguration() +EventPipeConfiguration *EventPipe::GetConfiguration() { LIMITED_METHOD_CONTRACT; return s_pConfig; } -CrstStatic* EventPipe::GetLock() +CrstStatic *EventPipe::GetLock() { LIMITED_METHOD_CONTRACT; @@ -955,7 +954,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv commandLine.Append((WCHAR)' '); commandLine.Append(pwzAssemblyPath); - for(int i=0; i<argc; i++) + for (int i = 0; i < argc; i++) { commandLine.Append((WCHAR)' '); commandLine.Append(argv[i]); @@ -971,7 +970,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv #endif } -EventPipeEventInstance* EventPipe::GetNextEvent() +EventPipeEventInstance *EventPipe::GetNextEvent() { CONTRACTL { @@ -993,253 +992,4 @@ EventPipeEventInstance* EventPipe::GetNextEvent() return pInstance; } -UINT64 QCALLTYPE EventPipeInternal::Enable( - __in_z LPCWSTR outputFile, - UINT32 circularBufferSizeInMB, - INT64 profilerSamplingRateInNanoseconds, - EventPipeProviderConfiguration *pProviders, - INT32 numProviders, - UINT64 multiFileTraceLengthInSeconds) -{ - QCALL_CONTRACT; - - UINT64 sessionID = 0; - - BEGIN_QCALL; - SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds); - sessionID = EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds); - END_QCALL; - - return sessionID; -} - -void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - EventPipe::Disable(sessionID); - END_QCALL; -} - -bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo) -{ - QCALL_CONTRACT; - - bool retVal = false; - BEGIN_QCALL; - - if(pSessionInfo != NULL) - { - EventPipeSession *pSession = EventPipe::GetSession(sessionID); - if(pSession != NULL) - { - pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime(); - pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart; - QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency); - retVal = true; - } - } - - END_QCALL; - return retVal; -} - -INT_PTR QCALLTYPE EventPipeInternal::CreateProvider( - __in_z LPCWSTR providerName, - EventPipeCallback pCallbackFunc) -{ - QCALL_CONTRACT; - - EventPipeProvider *pProvider = NULL; - - BEGIN_QCALL; - - pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL); - - END_QCALL; - - return reinterpret_cast<INT_PTR>(pProvider); -} - -INT_PTR QCALLTYPE EventPipeInternal::DefineEvent( - INT_PTR provHandle, - UINT32 eventID, - __int64 keywords, - UINT32 eventVersion, - UINT32 level, - void *pMetadata, - UINT32 metadataLength) -{ - QCALL_CONTRACT; - - EventPipeEvent *pEvent = NULL; - - BEGIN_QCALL; - - _ASSERTE(provHandle != NULL); - EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle); - pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength); - _ASSERTE(pEvent != NULL); - - END_QCALL; - - return reinterpret_cast<INT_PTR>(pEvent); -} - -INT_PTR QCALLTYPE EventPipeInternal::GetProvider( - __in_z LPCWSTR providerName) -{ - QCALL_CONTRACT; - - EventPipeProvider *pProvider = NULL; - - BEGIN_QCALL; - - pProvider = EventPipe::GetProvider(providerName); - - END_QCALL; - - return reinterpret_cast<INT_PTR>(pProvider); -} - -void QCALLTYPE EventPipeInternal::DeleteProvider( - INT_PTR provHandle) -{ - QCALL_CONTRACT; - BEGIN_QCALL; - - if(provHandle != NULL) - { - EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider*>(provHandle); - EventPipe::DeleteProvider(pProvider); - } - - END_QCALL; -} - -int QCALLTYPE EventPipeInternal::EventActivityIdControl( - uint controlCode, - GUID *pActivityId) -{ - - QCALL_CONTRACT; - - int retVal = 0; - - BEGIN_QCALL; - - Thread *pThread = GetThread(); - if(pThread == NULL || pActivityId == NULL) - { - retVal = 1; - } - else - { - ActivityControlCode activityControlCode = (ActivityControlCode)controlCode; - GUID currentActivityId; - switch(activityControlCode) - { - case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID: - - *pActivityId = *pThread->GetActivityId(); - break; - - case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID: - - pThread->SetActivityId(pActivityId); - break; - - case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID: - - CoCreateGuid(pActivityId); - break; - - case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID: - - currentActivityId = *pThread->GetActivityId(); - pThread->SetActivityId(pActivityId); - *pActivityId = currentActivityId; - - break; - - case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID: - - *pActivityId = *pThread->GetActivityId(); - CoCreateGuid(¤tActivityId); - pThread->SetActivityId(¤tActivityId); - break; - - default: - retVal = 1; - }; - } - - END_QCALL; - return retVal; -} - -void QCALLTYPE EventPipeInternal::WriteEvent( - INT_PTR eventHandle, - UINT32 eventID, - void *pData, - UINT32 length, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId) -{ - QCALL_CONTRACT; - BEGIN_QCALL; - - _ASSERTE(eventHandle != NULL); - EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); - EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId); - - END_QCALL; -} - -void QCALLTYPE EventPipeInternal::WriteEventData( - INT_PTR eventHandle, - UINT32 eventID, - EventData *pEventData, - UINT32 eventDataCount, - LPCGUID pActivityId, - LPCGUID pRelatedActivityId) -{ - QCALL_CONTRACT; - BEGIN_QCALL; - - _ASSERTE(eventHandle != NULL); - EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); - EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId); - - END_QCALL; -} - -bool QCALLTYPE EventPipeInternal::GetNextEvent( - EventPipeEventInstanceData *pInstance) -{ - QCALL_CONTRACT; - - EventPipeEventInstance *pNextInstance = NULL; - BEGIN_QCALL; - - _ASSERTE(pInstance != NULL); - - pNextInstance = EventPipe::GetNextEvent(); - if (pNextInstance) - { - pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider(); - pInstance->EventID = pNextInstance->GetEvent()->GetEventID(); - pInstance->ThreadID = pNextInstance->GetThreadId(); - pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart; - pInstance->ActivityId = *pNextInstance->GetActivityId(); - pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId(); - pInstance->Payload = pNextInstance->GetData(); - pInstance->PayloadLength = pNextInstance->GetDataLength(); - } - - END_QCALL; - return pNextInstance != NULL; -} - #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index c77b94dbdc..494a8c5c98 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -14,8 +14,6 @@ class EventPipeConfiguration; class EventPipeEvent; class EventPipeEventInstance; class EventPipeFile; -class EventPipeJsonFile; -class EventPipeBuffer; class EventPipeBufferManager; class EventPipeEventSource; class EventPipeProvider; @@ -35,13 +33,13 @@ struct EventFilterDescriptor ULONGLONG Ptr; // The size of the filter data, in bytes. The maximum size is 1024 bytes. - ULONG Size; + 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; + ULONG Type; }; // Define the event pipe callback to match the ETW callback signature. @@ -56,7 +54,6 @@ typedef void (*EventPipeCallback)( struct EventData { -public: UINT64 Ptr; unsigned int Size; unsigned int Reserved; @@ -91,7 +88,7 @@ public: // 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(); + BYTE *GetFlatData(); // Return true is the data is stored in a flat buffer bool IsFlattened() const @@ -109,7 +106,7 @@ public: return m_size; } - EventData* GetEventDataArray() const + EventData *GetEventDataArray() const { LIMITED_METHOD_CONTRACT; @@ -120,7 +117,6 @@ public: class StackContents { private: - const static unsigned int MAX_STACK_DEPTH = 100; // Array of IP values from a stack crawl. @@ -130,14 +126,13 @@ private: #ifdef _DEBUG // Parallel array of MethodDesc pointers. // Used for debug-only stack printing. - MethodDesc* m_methods[MAX_STACK_DEPTH]; + MethodDesc *m_methods[MAX_STACK_DEPTH]; #endif // _DEBUG // The next available slot in StackFrames. unsigned int m_nextAvailableFrame; public: - StackContents() { LIMITED_METHOD_CONTRACT; @@ -152,7 +147,7 @@ public: memcpy_s(pDest->m_stackFrames, MAX_STACK_DEPTH * sizeof(UINT_PTR), m_stackFrames, sizeof(UINT_PTR) * m_nextAvailableFrame); #ifdef _DEBUG - memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc*), m_methods, sizeof(MethodDesc*) * m_nextAvailableFrame); + memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc *), m_methods, sizeof(MethodDesc *) * m_nextAvailableFrame); #endif pDest->m_nextAvailableFrame = m_nextAvailableFrame; } @@ -192,7 +187,7 @@ public: } #ifdef _DEBUG - MethodDesc* GetMethod(unsigned int frameIndex) + MethodDesc *GetMethod(unsigned int frameIndex) { LIMITED_METHOD_CONTRACT; _ASSERTE(frameIndex < MAX_STACK_DEPTH); @@ -210,7 +205,7 @@ public: { LIMITED_METHOD_CONTRACT; - if(m_nextAvailableFrame < MAX_STACK_DEPTH) + if (m_nextAvailableFrame < MAX_STACK_DEPTH) { m_stackFrames[m_nextAvailableFrame] = controlPC; #ifdef _DEBUG @@ -220,11 +215,11 @@ public: } } - BYTE* GetPointer() const + BYTE *GetPointer() const { LIMITED_METHOD_CONTRACT; - return (BYTE*)m_stackFrames; + return (BYTE *)m_stackFrames; } unsigned int GetSize() const @@ -246,146 +241,128 @@ class EventPipe friend class EventPipeBufferManager; friend class SampleProfiler; - public: - - // Initialize the event pipe. - static void Initialize(); - - // Shutdown the event pipe. - static void Shutdown(); - - // Enable tracing via the event pipe. - static EventPipeSessionID Enable( - LPCWSTR strOutputPath, - unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - int numProviders, - UINT64 multiFileTraceLengthInSeconds); - - // Disable tracing via the event pipe. - static void Disable(EventPipeSessionID id); - - // Get the session for the specified session ID. - static EventPipeSession* GetSession(EventPipeSessionID id); - - // Specifies whether or not the event pipe is enabled. - static bool Enabled(); - - // Create a provider. - static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL); - - // Get a provider. - static EventPipeProvider* GetProvider(const SString &providerName); - - // 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); +public: + // Initialize the event pipe. + static void Initialize(); - // Get the managed call stack for the specified thread. - static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents); + // Shutdown the event pipe. + static void Shutdown(); - // Save the command line for the current process. - static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv); + // Enable tracing via the event pipe. + static EventPipeSessionID Enable( + LPCWSTR strOutputPath, + uint32_t circularBufferSizeInMB, + uint64_t profilerSamplingRateInNanoseconds, + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds); - // Get next event. - static EventPipeEventInstance* GetNextEvent(); + // Disable tracing via the event pipe. + static void Disable(EventPipeSessionID id); - protected: + // Get the session for the specified session ID. + static EventPipeSession *GetSession(EventPipeSessionID id); - // The counterpart to WriteEvent which after the payload is constructed - static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); + // Specifies whether or not the event pipe is enabled. + static bool Enabled(); - private: + // Create a provider. + static EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL); - // Enable the specified EventPipe session. - static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); + // Get a provider. + static EventPipeProvider *GetProvider(const SString &providerName); - static void CreateFileSwitchTimer(); + // Delete a provider. + static void DeleteProvider(EventPipeProvider *pProvider); - static void DeleteFileSwitchTimer(); + // 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); - // Performs one polling operation to determine if it is necessary to switch to a new file. - // If the polling operation decides it is time, it will perform the switch. - // Called directly from the timer when the timer is triggered. - static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired); + // 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); - // If event pipe has been configured to write multiple files, switch to the next file. - static void SwitchToNextFile(); + // Write out a sample profile event. + static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0); - // Generate the file path for the next trace file. - // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time. - static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath); + // Get the managed call stack for the current thread. + static bool WalkManagedStackForCurrentThread(StackContents &stackContents); - // Callback function for the stack walker. For each frame walked, this callback is invoked. - static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData); + // Get the managed call stack for the specified thread. + static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents); - // Get the configuration object. - // This is called directly by the EventPipeProvider constructor to register the new provider. - static EventPipeConfiguration* GetConfiguration(); + // Save the command line for the current process. + static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv); - // Get the event pipe configuration lock. - static CrstStatic* GetLock(); + // Get next event. + static EventPipeEventInstance *GetNextEvent(); - static CrstStatic s_configCrst; - static bool s_tracingInitialized; - static EventPipeConfiguration *s_pConfig; - static EventPipeSession *s_pSession; - static EventPipeBufferManager *s_pBufferManager; - static LPCWSTR s_pOutputPath; - static unsigned long s_nextFileIndex; - static EventPipeFile *s_pFile; - static EventPipeEventSource *s_pEventSource; - static LPCWSTR s_pCommandLine; - const static DWORD FileSwitchTimerPeriodMS = 1000; - static HANDLE s_fileSwitchTimerHandle; - static ULONGLONG s_lastFileSwitchTime; +private: + // The counterpart to WriteEvent which after the payload is constructed + static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL); + + // Enable the specified EventPipe session. + static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession); + + static void CreateFileSwitchTimer(); + + static void DeleteFileSwitchTimer(); + + // Performs one polling operation to determine if it is necessary to switch to a new file. + // If the polling operation decides it is time, it will perform the switch. + // Called directly from the timer when the timer is triggered. + static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired); + + // If event pipe has been configured to write multiple files, switch to the next file. + static void SwitchToNextFile(); + + // Generate the file path for the next trace file. + // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time. + static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath); + + // Callback function for the stack walker. For each frame walked, this callback is invoked. + static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData); + + // Get the configuration object. + // This is called directly by the EventPipeProvider constructor to register the new provider. + static EventPipeConfiguration *GetConfiguration(); + + // Get the event pipe configuration lock. + static CrstStatic *GetLock(); + + static CrstStatic s_configCrst; + static bool s_tracingInitialized; + static EventPipeConfiguration *s_pConfig; + static EventPipeSession *s_pSession; + static EventPipeBufferManager *s_pBufferManager; + static LPCWSTR s_pOutputPath; + static unsigned long s_nextFileIndex; + static EventPipeFile *s_pFile; + static EventPipeEventSource *s_pEventSource; + static LPCWSTR s_pCommandLine; + const static DWORD FileSwitchTimerPeriodMS = 1000; + static HANDLE s_fileSwitchTimerHandle; + static ULONGLONG s_lastFileSwitchTime; }; struct EventPipeProviderConfiguration { - private: - - LPCWSTR m_pProviderName; - UINT64 m_keywords; - UINT32 m_loggingLevel; - LPCWSTR m_pFilterData; + LPCWSTR m_pProviderName = nullptr; + UINT64 m_keywords = 0; + UINT32 m_loggingLevel = 0; + LPCWSTR m_pFilterData = nullptr; public: + EventPipeProviderConfiguration() = default; - EventPipeProviderConfiguration() + EventPipeProviderConfiguration(LPCWSTR pProviderName, UINT64 keywords, UINT32 loggingLevel, LPCWSTR pFilterData) : + m_pProviderName(pProviderName), + m_keywords(keywords), + m_loggingLevel(loggingLevel), + m_pFilterData(pFilterData) { - LIMITED_METHOD_CONTRACT; - m_pProviderName = NULL; - m_keywords = NULL; - m_loggingLevel = 0; - m_pFilterData = NULL; - } - - EventPipeProviderConfiguration( - LPCWSTR pProviderName, - UINT64 keywords, - UINT32 loggingLevel, - LPCWSTR pFilterData) - { - LIMITED_METHOD_CONTRACT; - m_pProviderName = pProviderName; - m_keywords = keywords; - m_loggingLevel = loggingLevel; - m_pFilterData = pFilterData; } LPCWSTR GetProviderName() const @@ -413,95 +390,6 @@ public: } }; -class EventPipeInternal -{ -private: - - enum class ActivityControlCode - { - EVENT_ACTIVITY_CONTROL_GET_ID = 1, - EVENT_ACTIVITY_CONTROL_SET_ID = 2, - EVENT_ACTIVITY_CONTROL_CREATE_ID = 3, - EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4, - EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5 - }; - - struct EventPipeEventInstanceData - { - public: - void *ProviderID; - unsigned int EventID; - unsigned int ThreadID; - LARGE_INTEGER TimeStamp; - GUID ActivityId; - GUID RelatedActivityId; - const BYTE *Payload; - unsigned int PayloadLength; - }; - - struct EventPipeSessionInfo - { - public: - FILETIME StartTimeAsUTCFileTime; - LARGE_INTEGER StartTimeStamp; - LARGE_INTEGER TimeStampFrequency; - }; - -public: - - static UINT64 QCALLTYPE Enable( - __in_z LPCWSTR outputFile, - UINT32 circularBufferSizeInMB, - INT64 profilerSamplingRateInNanoseconds, - EventPipeProviderConfiguration *pProviders, - INT32 numProviders, - UINT64 multiFileTraceLengthInSeconds); - - static void QCALLTYPE Disable(UINT64 sessionID); - - static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo); - - static INT_PTR QCALLTYPE CreateProvider( - __in_z LPCWSTR providerName, - EventPipeCallback pCallbackFunc); - - static INT_PTR QCALLTYPE DefineEvent( - INT_PTR provHandle, - UINT32 eventID, - __int64 keywords, - UINT32 eventVersion, - UINT32 level, - void *pMetadata, - UINT32 metadataLength); - - static INT_PTR QCALLTYPE GetProvider( - __in_z LPCWSTR providerName); - - static void QCALLTYPE DeleteProvider( - INT_PTR provHandle); - - static int QCALLTYPE EventActivityIdControl( - uint controlCode, - GUID *pActivityId); - - static void QCALLTYPE WriteEvent( - INT_PTR eventHandle, - UINT32 eventID, - void *pData, - UINT32 length, - LPCGUID pActivityId, LPCGUID pRelatedActivityId); - - static void QCALLTYPE WriteEventData( - INT_PTR eventHandle, - UINT32 eventID, - EventData *pEventData, - UINT32 eventDataCount, - LPCGUID pActivityId, LPCGUID pRelatedActivityId); - - static bool QCALLTYPE GetNextEvent( - EventPipeEventInstanceData *pInstance); -}; - #endif // FEATURE_PERFTRACING #endif // __EVENTPIPE_H__ diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp index 9cf8280722..4667276753 100644 --- a/src/vm/eventpipeconfiguration.cpp +++ b/src/vm/eventpipeconfiguration.cpp @@ -6,12 +6,13 @@ #include "eventpipe.h" #include "eventpipeconfiguration.h" #include "eventpipeeventinstance.h" +#include "eventpipesessionprovider.h" #include "eventpipeprovider.h" #include "eventpipesession.h" #ifdef FEATURE_PERFTRACING -const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration"); +const WCHAR *EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration"); EventPipeConfiguration::EventPipeConfiguration() { @@ -22,7 +23,7 @@ EventPipeConfiguration::EventPipeConfiguration() m_pRundownThread = NULL; m_pConfigProvider = NULL; m_pSession = NULL; - m_pProviderList = new SList<SListElem<EventPipeProvider*>>(); + m_pProviderList = new SList<SListElem<EventPipeProvider *>>(); } EventPipeConfiguration::~EventPipeConfiguration() @@ -35,25 +36,25 @@ EventPipeConfiguration::~EventPipeConfiguration() } CONTRACTL_END; - if(m_pConfigProvider != NULL) + if (m_pConfigProvider != NULL) { // This unregisters the provider, which takes a // HOST_BREAKABLE lock EX_TRY { - DeleteProvider(m_pConfigProvider); - m_pConfigProvider = NULL; + DeleteProvider(m_pConfigProvider); + m_pConfigProvider = NULL; } - EX_CATCH { } + EX_CATCH {} EX_END_CATCH(SwallowAllExceptions); } - if(m_pSession != NULL) + if (m_pSession != NULL) { DeleteSession(m_pSession); m_pSession = NULL; } - if(m_pProviderList != NULL) + if (m_pProviderList != NULL) { // We swallow exceptions here because the HOST_BREAKABLE // lock may throw and this destructor gets called in throw @@ -63,18 +64,18 @@ EventPipeConfiguration::~EventPipeConfiguration() // Take the lock before manipulating the list. CrstHolder _crst(EventPipe::GetLock()); - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { // We don't delete provider itself because it can be in-use - SListElem<EventPipeProvider*> *pCurElem = pElem; + SListElem<EventPipeProvider *> *pCurElem = pElem; pElem = m_pProviderList->GetNext(pElem); - delete(pCurElem); + delete (pCurElem); } - delete(m_pProviderList); + delete (m_pProviderList); } - EX_CATCH { } + EX_CATCH {} EX_END_CATCH(SwallowAllExceptions); m_pProviderList = NULL; @@ -96,14 +97,14 @@ void EventPipeConfiguration::Initialize() // Create the metadata event. m_pMetadataEvent = m_pConfigProvider->AddEvent( - 0, /* eventID */ - 0, /* keywords */ - 0, /* eventVersion */ + 0, /* eventID */ + 0, /* keywords */ + 0, /* eventVersion */ EventPipeEventLevel::LogAlways, false); /* needStack */ } -EventPipeProvider* EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) +EventPipeProvider *EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData) { CONTRACTL { @@ -134,18 +135,15 @@ void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider) CONTRACTL_END; if (pProvider == NULL) - { return; - } // Unregister the provider. UnregisterProvider(*pProvider); // Free the provider itself. - delete(pProvider); + delete pProvider; } - bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider) { CONTRACTL @@ -161,7 +159,7 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider) // See if we've already registered this provider. EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName()); - if(pExistingProvider != NULL) + if (pExistingProvider != NULL) { return false; } @@ -170,14 +168,14 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider) if (m_pProviderList != NULL) { // The provider has not been registered, so register it. - m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider)); + m_pProviderList->InsertTail(new SListElem<EventPipeProvider *>(&provider)); } // Set the provider configuration and enable it if it has been requested by a session. - if(m_pSession != NULL) + if (m_pSession != NULL) { EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, &provider); - if(pSessionProvider != NULL) + if (pSessionProvider != NULL) { provider.SetConfiguration( true /* providerEnabled */, @@ -207,10 +205,10 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider) if (m_pProviderList != NULL) { // Find the provider. - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { - if(pElem->GetValue() == &provider) + if (pElem->GetValue() == &provider) { break; } @@ -219,11 +217,11 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider) } // If we found the provider, remove it. - if(pElem != NULL) + if (pElem != NULL) { - if(m_pProviderList->FindAndRemove(pElem) != NULL) + if (m_pProviderList->FindAndRemove(pElem) != NULL) { - delete(pElem); + delete (pElem); return true; } } @@ -232,7 +230,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider) return false; } -EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName) +EventPipeProvider *EventPipeConfiguration::GetProvider(const SString &providerName) { CONTRACTL { @@ -249,7 +247,7 @@ EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerNa return GetProviderNoLock(providerName); } -EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName) +EventPipeProvider *EventPipeConfiguration::GetProviderNoLock(const SString &providerName) { CONTRACTL { @@ -263,11 +261,11 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov // The provider list should be non-NULL, but can be NULL on shutdown. if (m_pProviderList != NULL) { - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { EventPipeProvider *pProvider = pElem->GetValue(); - if(pProvider->GetProviderName().Equals(providerName)) + if (pProvider->GetProviderName().Equals(providerName)) { return pProvider; } @@ -279,7 +277,7 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov return NULL; } -EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider) +EventPipeSessionProvider *EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider) { CONTRACTL { @@ -291,9 +289,9 @@ EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSe CONTRACTL_END; EventPipeSessionProvider *pRet = NULL; - if(pSession != NULL) + if (pSession != NULL) { - pRet = pSession->GetSessionProvider(pProvider); + pRet = pSession->GetSessionProvider(pProvider); } return pRet; } @@ -303,24 +301,35 @@ size_t EventPipeConfiguration::GetCircularBufferSize() const LIMITED_METHOD_CONTRACT; size_t ret = 0; - if(m_pSession != NULL) + if (m_pSession != NULL) { ret = m_pSession->GetCircularBufferSize(); } return ret; } -EventPipeSession* EventPipeConfiguration::CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds) +EventPipeSession *EventPipeConfiguration::CreateSession( + EventPipeSessionType sessionType, + unsigned int circularBufferSizeInMB, + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; + PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr)); } CONTRACTL_END; - return new EventPipeSession(sessionType, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds); + return new EventPipeSession( + sessionType, + circularBufferSizeInMB, + pProviders, + numProviders, + multiFileTraceLengthInSeconds); } void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession) @@ -336,9 +345,9 @@ void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession) CONTRACTL_END; // TODO: Multiple session support will require individual enabled bits. - if(pSession != NULL && !m_enabled) + if (pSession != NULL && !m_enabled) { - delete(pSession); + delete (pSession); } } @@ -361,14 +370,14 @@ void EventPipeConfiguration::Enable(EventPipeSession *pSession) // The provider list should be non-NULL, but can be NULL on shutdown. if (m_pProviderList != NULL) { - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { EventPipeProvider *pProvider = pElem->GetValue(); // Enable the provider if it has been configured. EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, pProvider); - if(pSessionProvider != NULL) + if (pSessionProvider != NULL) { pProvider->SetConfiguration( true /* providerEnabled */, @@ -400,8 +409,8 @@ void EventPipeConfiguration::Disable(EventPipeSession *pSession) // The provider list should be non-NULL, but can be NULL on shutdown. if (m_pProviderList != NULL) { - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { EventPipeProvider *pProvider = pElem->GetValue(); pProvider->SetConfiguration( @@ -458,7 +467,7 @@ void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession) Enable(pSession); } -EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId) +EventPipeEventInstance *EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId) { CONTRACTL { @@ -490,7 +499,7 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip memcpy(currentPtr, &metadataId, sizeof(metadataId)); currentPtr += sizeof(metadataId); - memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength); + memcpy(currentPtr, (BYTE *)providerName.GetUnicode(), providerNameLength); currentPtr += providerNameLength; // Write the incoming payload data. @@ -523,19 +532,18 @@ void EventPipeConfiguration::DeleteDeferredProviders() MODE_ANY; // Lock must be held by EventPipe::Disable. PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); - } CONTRACTL_END; // The provider list should be non-NULL, but can be NULL on shutdown. if (m_pProviderList != NULL) { - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead(); + while (pElem != NULL) { EventPipeProvider *pProvider = pElem->GetValue(); pElem = m_pProviderList->GetNext(pElem); - if(pProvider->GetDeleteDeferred()) + if (pProvider->GetDeleteDeferred()) { DeleteProvider(pProvider); } diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h index 5eec76ca80..6021305b82 100644 --- a/src/vm/eventpipeconfiguration.h +++ b/src/vm/eventpipeconfiguration.h @@ -13,10 +13,8 @@ class EventPipeSessionProvider; class EventPipeEvent; class EventPipeEventInstance; class EventPipeProvider; -struct EventPipeProviderConfiguration; class EventPipeSession; enum class EventPipeSessionType; -class EventPipeSessionProvider; enum class EventPipeEventLevel { @@ -31,7 +29,6 @@ enum class EventPipeEventLevel class EventPipeConfiguration { public: - EventPipeConfiguration(); ~EventPipeConfiguration(); @@ -39,7 +36,7 @@ public: void Initialize(); // Create a new provider. - EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData); + EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData); // Delete a provider. void DeleteProvider(EventPipeProvider *pProvider); @@ -51,10 +48,15 @@ public: bool UnregisterProvider(EventPipeProvider &provider); // Get the provider with the specified provider ID if it exists. - EventPipeProvider* GetProvider(const SString &providerID); + EventPipeProvider *GetProvider(const SString &providerID); // Create a new session. - EventPipeSession* CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds = 0); + EventPipeSession *CreateSession( + EventPipeSessionType sessionType, + unsigned int circularBufferSizeInMB, + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds = 0); // Delete a session. void DeleteSession(EventPipeSession *pSession); @@ -78,7 +80,7 @@ public: void EnableRundown(EventPipeSession *pSession); // Get the event used to write metadata to the event stream. - EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId); + EventPipeEventInstance *BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId); // Delete deferred providers. void DeleteDeferredProviders(); @@ -93,12 +95,11 @@ public: } private: - // Get the provider without taking the lock. - EventPipeProvider* GetProviderNoLock(const SString &providerID); + EventPipeProvider *GetProviderNoLock(const SString &providerID); // Get the enabled provider. - EventPipeSessionProvider* GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider); + EventPipeSessionProvider *GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider); // The one and only EventPipe session. EventPipeSession *m_pSession; @@ -107,7 +108,7 @@ private: Volatile<bool> m_enabled; // The list of event pipe providers. - SList<SListElem<EventPipeProvider*>> *m_pProviderList; + SList<SListElem<EventPipeProvider *>> *m_pProviderList; // The provider used to write configuration events to the event stream. EventPipeProvider *m_pConfigProvider; @@ -117,7 +118,7 @@ private: // The provider name for the configuration event pipe provider. // This provider is used to emit configuration events. - const static WCHAR* s_configurationProviderName; + const static WCHAR *s_configurationProviderName; // True if rundown is enabled. Volatile<bool> m_rundownEnabled; diff --git a/src/vm/eventpipeeventinstance.h b/src/vm/eventpipeeventinstance.h index 80fd49d946..cf995f2534 100644 --- a/src/vm/eventpipeeventinstance.h +++ b/src/vm/eventpipeeventinstance.h @@ -14,6 +14,8 @@ #include "fastserializableobject.h" #include "fastserializer.h" +class EventPipeJsonFile; + class EventPipeEventInstance { // Declare friends. diff --git a/src/vm/eventpipeeventsource.cpp b/src/vm/eventpipeeventsource.cpp index 20dc5b2686..20613b8d53 100644 --- a/src/vm/eventpipeeventsource.cpp +++ b/src/vm/eventpipeeventsource.cpp @@ -9,6 +9,7 @@ #include "eventpipemetadatagenerator.h" #include "eventpipeprovider.h" #include "eventpipesession.h" +#include "eventpipesessionprovider.h" #ifdef FEATURE_PERFTRACING diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp index 424bbef5ad..2e98fd5ad7 100644 --- a/src/vm/eventpipefile.cpp +++ b/src/vm/eventpipefile.cpp @@ -11,8 +11,7 @@ #ifdef FEATURE_PERFTRACING -EventPipeFile::EventPipeFile( - SString &outputFilePath) +EventPipeFile::EventPipeFile(SString &outputFilePath) { CONTRACTL { @@ -43,7 +42,7 @@ EventPipeFile::EventPipeFile( m_samplingRateInNs = SampleProfiler::GetSamplingRate(); // Create the file stream and write the header. - m_pSerializer = new FastSerializer(outputFilePath); + m_pSerializer = new FastSerializer(new FileStreamWriter(outputFilePath)); m_serializationLock.Init(LOCK_TYPE_DEFAULT); m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>(); @@ -101,7 +100,7 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance) metadataId = GenerateMetadataId(); EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId); - + WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event. SaveMetadataId(*instance.GetEvent(), metadataId); @@ -129,7 +128,7 @@ void EventPipeFile::WriteEnd() // "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); + m_pSerializer->WriteTag(FastSerializerTags::NullReference); } void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId) diff --git a/src/vm/eventpipeinternal.cpp b/src/vm/eventpipeinternal.cpp new file mode 100644 index 0000000000..c02317f491 --- /dev/null +++ b/src/vm/eventpipeinternal.cpp @@ -0,0 +1,270 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "eventpipe.h" +#include "eventpipeconfiguration.h" +#include "eventpipeeventinstance.h" +#include "eventpipeinternal.h" +#include "eventpipeprovider.h" +#include "eventpipesession.h" +#include "eventpipesessionprovider.h" + +#ifdef FEATURE_PAL +#include "pal.h" +#endif // FEATURE_PAL + +#ifdef FEATURE_PERFTRACING + +UINT64 QCALLTYPE EventPipeInternal::Enable( + __in_z LPCWSTR outputFile, + UINT32 circularBufferSizeInMB, + INT64 profilerSamplingRateInNanoseconds, + EventPipeProviderConfiguration *pProviders, + UINT32 numProviders, + UINT64 multiFileTraceLengthInSeconds) +{ + QCALL_CONTRACT; + + UINT64 sessionID = 0; + + BEGIN_QCALL; + { + sessionID = EventPipe::Enable( + outputFile, + circularBufferSizeInMB, + profilerSamplingRateInNanoseconds, + pProviders, + numProviders, + multiFileTraceLengthInSeconds); + } + END_QCALL; + + return sessionID; +} + +void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + EventPipe::Disable(sessionID); + END_QCALL; +} + +bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo) +{ + QCALL_CONTRACT; + + bool retVal = false; + BEGIN_QCALL; + + if (pSessionInfo != NULL) + { + EventPipeSession *pSession = EventPipe::GetSession(sessionID); + if (pSession != NULL) + { + pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime(); + pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart; + QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency); + retVal = true; + } + } + + END_QCALL; + return retVal; +} + +INT_PTR QCALLTYPE EventPipeInternal::CreateProvider( + __in_z LPCWSTR providerName, + EventPipeCallback pCallbackFunc) +{ + QCALL_CONTRACT; + + EventPipeProvider *pProvider = NULL; + + BEGIN_QCALL; + + pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL); + + END_QCALL; + + return reinterpret_cast<INT_PTR>(pProvider); +} + +INT_PTR QCALLTYPE EventPipeInternal::DefineEvent( + INT_PTR provHandle, + UINT32 eventID, + __int64 keywords, + UINT32 eventVersion, + UINT32 level, + void *pMetadata, + UINT32 metadataLength) +{ + QCALL_CONTRACT; + + EventPipeEvent *pEvent = NULL; + + BEGIN_QCALL; + + _ASSERTE(provHandle != NULL); + EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle); + pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength); + _ASSERTE(pEvent != NULL); + + END_QCALL; + + return reinterpret_cast<INT_PTR>(pEvent); +} + +INT_PTR QCALLTYPE EventPipeInternal::GetProvider(__in_z LPCWSTR providerName) +{ + QCALL_CONTRACT; + + EventPipeProvider *pProvider = NULL; + + BEGIN_QCALL; + + pProvider = EventPipe::GetProvider(providerName); + + END_QCALL; + + return reinterpret_cast<INT_PTR>(pProvider); +} + +void QCALLTYPE EventPipeInternal::DeleteProvider(INT_PTR provHandle) +{ + QCALL_CONTRACT; + BEGIN_QCALL; + + if (provHandle != NULL) + { + EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle); + EventPipe::DeleteProvider(pProvider); + } + + END_QCALL; +} + +int QCALLTYPE EventPipeInternal::EventActivityIdControl(uint32_t controlCode, GUID *pActivityId) +{ + + QCALL_CONTRACT; + + int retVal = 0; + + BEGIN_QCALL; + + Thread *pThread = GetThread(); + if (pThread == NULL || pActivityId == NULL) + { + retVal = 1; + } + else + { + ActivityControlCode activityControlCode = (ActivityControlCode)controlCode; + GUID currentActivityId; + switch (activityControlCode) + { + case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID: + + *pActivityId = *pThread->GetActivityId(); + break; + + case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID: + + pThread->SetActivityId(pActivityId); + break; + + case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID: + + CoCreateGuid(pActivityId); + break; + + case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID: + + currentActivityId = *pThread->GetActivityId(); + pThread->SetActivityId(pActivityId); + *pActivityId = currentActivityId; + break; + + case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID: + + *pActivityId = *pThread->GetActivityId(); + CoCreateGuid(¤tActivityId); + pThread->SetActivityId(¤tActivityId); + break; + + default: + retVal = 1; + } + } + + END_QCALL; + return retVal; +} + +void QCALLTYPE EventPipeInternal::WriteEvent( + INT_PTR eventHandle, + UINT32 eventID, + void *pData, + UINT32 length, + LPCGUID pActivityId, + LPCGUID pRelatedActivityId) +{ + QCALL_CONTRACT; + BEGIN_QCALL; + + _ASSERTE(eventHandle != NULL); + EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); + EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId); + + END_QCALL; +} + +void QCALLTYPE EventPipeInternal::WriteEventData( + INT_PTR eventHandle, + UINT32 eventID, + EventData *pEventData, + UINT32 eventDataCount, + LPCGUID pActivityId, + LPCGUID pRelatedActivityId) +{ + QCALL_CONTRACT; + BEGIN_QCALL; + + _ASSERTE(eventHandle != NULL); + EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle); + EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId); + + END_QCALL; +} + +bool QCALLTYPE EventPipeInternal::GetNextEvent(EventPipeEventInstanceData *pInstance) +{ + QCALL_CONTRACT; + + EventPipeEventInstance *pNextInstance = NULL; + BEGIN_QCALL; + + _ASSERTE(pInstance != NULL); + + pNextInstance = EventPipe::GetNextEvent(); + if (pNextInstance) + { + pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider(); + pInstance->EventID = pNextInstance->GetEvent()->GetEventID(); + pInstance->ThreadID = pNextInstance->GetThreadId(); + pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart; + pInstance->ActivityId = *pNextInstance->GetActivityId(); + pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId(); + pInstance->Payload = pNextInstance->GetData(); + pInstance->PayloadLength = pNextInstance->GetDataLength(); + } + + END_QCALL; + return pNextInstance != NULL; +} + +#endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipeinternal.h b/src/vm/eventpipeinternal.h new file mode 100644 index 0000000000..bbd4ad633b --- /dev/null +++ b/src/vm/eventpipeinternal.h @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __EVENTPIPEINTERNAL_H__ +#define __EVENTPIPEINTERNAL_H__ + +#ifdef FEATURE_PERFTRACING + +// TODO: Maybe we should move the other types that are used on PInvoke here? + +class EventPipeInternal +{ +private: + enum class ActivityControlCode + { + EVENT_ACTIVITY_CONTROL_GET_ID = 1, + EVENT_ACTIVITY_CONTROL_SET_ID = 2, + EVENT_ACTIVITY_CONTROL_CREATE_ID = 3, + EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4, + EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5 + }; + + struct EventPipeEventInstanceData + { + void *ProviderID; + unsigned int EventID; + unsigned int ThreadID; + LARGE_INTEGER TimeStamp; + GUID ActivityId; + GUID RelatedActivityId; + const BYTE *Payload; + unsigned int PayloadLength; + }; + + struct EventPipeSessionInfo + { + FILETIME StartTimeAsUTCFileTime; + LARGE_INTEGER StartTimeStamp; + LARGE_INTEGER TimeStampFrequency; + }; + +public: + //! + //! Sets the sampling rate and enables the event pipe for the specified configuration. + //! + static UINT64 QCALLTYPE Enable( + __in_z LPCWSTR outputFile, + UINT32 circularBufferSizeInMB, + INT64 profilerSamplingRateInNanoseconds, + EventPipeProviderConfiguration *pProviders, + UINT32 numProviders, + UINT64 multiFileTraceLengthInSeconds); + + //! TODO: Add a ListActiveSessions to get the live SessionID in order to Disable? + + //! + //! Disables the specified session Id. + //! + static void QCALLTYPE Disable(UINT64 sessionID); + + static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo); + + static INT_PTR QCALLTYPE CreateProvider( + __in_z LPCWSTR providerName, + EventPipeCallback pCallbackFunc); + + static INT_PTR QCALLTYPE DefineEvent( + INT_PTR provHandle, + UINT32 eventID, + __int64 keywords, + UINT32 eventVersion, + UINT32 level, + void *pMetadata, + UINT32 metadataLength); + + static INT_PTR QCALLTYPE GetProvider( + __in_z LPCWSTR providerName); + + static void QCALLTYPE DeleteProvider( + INT_PTR provHandle); + + static int QCALLTYPE EventActivityIdControl( + uint32_t controlCode, + GUID *pActivityId); + + static void QCALLTYPE WriteEvent( + INT_PTR eventHandle, + UINT32 eventID, + void *pData, + UINT32 length, + LPCGUID pActivityId, LPCGUID pRelatedActivityId); + + static void QCALLTYPE WriteEventData( + INT_PTR eventHandle, + UINT32 eventID, + EventData *pEventData, + UINT32 eventDataCount, + LPCGUID pActivityId, LPCGUID pRelatedActivityId); + + static bool QCALLTYPE GetNextEvent( + EventPipeEventInstanceData *pInstance); +}; + +#endif // FEATURE_PERFTRACING + +#endif // __EVENTPIPEINTERNAL_H__ diff --git a/src/vm/eventpipeprotocolhelper.cpp b/src/vm/eventpipeprotocolhelper.cpp new file mode 100644 index 0000000000..bfcdcfa2f8 --- /dev/null +++ b/src/vm/eventpipeprotocolhelper.cpp @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "eventpipeprotocolhelper.h" +#include "diagnosticsipc.h" +#include "diagnosticsprotocol.h" + +#ifdef FEATURE_PERFTRACING + +bool EventPipeProtocolHelper::TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &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 (wcslen(pProviderName) == 0) + return false; // TODO: Should we ignore these input? + + LPCWSTR pFilterData = nullptr; // This parameter is optional. + TryParseString(bufferCursor, bufferLen, pFilterData); + + pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData); + } + return true; +} + +void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(pStream != nullptr); + } + CONTRACTL_END; + + // TODO: Read within a loop. + const uint32_t BufferSize = 8192; + uint8_t buffer[BufferSize]{}; + uint32_t nNumberOfBytesRead = 0; + bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead); + if (!fSuccess) + { + // TODO: Add error handling. + delete pStream; + return; + } + + // 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, ulong multiFileTraceLength, string outputPath, array<provider_config> providers + // uint = 4 little endian bytes + // ulong = 8 little endian bytes + // wchar = 2 little endian bytes, UTF16 encoding + // array<T> = uint length, length # of Ts + // string = (array<char> where the last char must = 0) or (length = 0) + // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data + + LPCWSTR strOutputPath; + uint32_t circularBufferSizeInMB = EventPipeProtocolHelper::DefaultCircularBufferMB; + uint64_t multiFileTraceLengthInSeconds = EventPipeProtocolHelper::DefaultMultiFileTraceLengthInSeconds; + CQuickArray<EventPipeProviderConfiguration> providerConfigs; + + uint8_t *pBufferCursor = buffer; + uint32_t bufferLen = nNumberOfBytesRead; + if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) || + !TryParse(pBufferCursor, bufferLen, multiFileTraceLengthInSeconds) || + !TryParseString(pBufferCursor, bufferLen, strOutputPath) || + !TryParseProviderConfigurations(pBufferCursor, bufferLen, providerConfigs)) + { + return; // TODO: error handling + } + + EventPipeSessionID sessionId = (EventPipeSessionID) nullptr; + if (providerConfigs.Size() > 0) + { + sessionId = EventPipe::Enable( + strOutputPath, // outputFile + circularBufferSizeInMB, // circularBufferSizeInMB + DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds + providerConfigs.Ptr(), // pConfigs + static_cast<uint32_t>(providerConfigs.Size()), // numConfigs + multiFileTraceLengthInSeconds); // multiFileTraceLengthInSeconds + } + + uint32_t nBytesWritten = 0; + fSuccess = pStream->Write(&sessionId, sizeof(sessionId), nBytesWritten); + if (!fSuccess) + { + // TODO: Add error handling. + delete pStream; + return; + } + + fSuccess = pStream->Flush(); + if (!fSuccess) + { + // TODO: Add error handling. + } + delete pStream; +} + +void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(pStream != nullptr); + } + CONTRACTL_END; + + uint32_t nNumberOfBytesRead = 0; + EventPipeSessionID sessionId = (EventPipeSessionID) nullptr; + const bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead); + if (!fSuccess || nNumberOfBytesRead != sizeof(sessionId)) + { + // TODO: Add error handling. + delete pStream; + return; + } + + EventPipe::Disable(sessionId); + // TODO: Should we acknowledge back? + delete pStream; +} + +#endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipeprotocolhelper.h b/src/vm/eventpipeprotocolhelper.h new file mode 100644 index 0000000000..fb3f65a247 --- /dev/null +++ b/src/vm/eventpipeprotocolhelper.h @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __EVENTPIPE_PROTOCOL_HELPER_H__ +#define __EVENTPIPE_PROTOCOL_HELPER_H__ + +#ifdef FEATURE_PERFTRACING + +#include "common.h" +#include "eventpipe.h" + +class IpcStream; + +class EventPipeProtocolHelper +{ +public: + // IPC event handlers. + static void EnableFileTracingEventHandler(IpcStream *pStream); + static void DisableTracingEventHandler(IpcStream *pStream); + +private: + const static uint32_t DefaultCircularBufferMB = 1024; // 1 GB + const static uint64_t DefaultMultiFileTraceLengthInSeconds = 0; + const static uint32_t DefaultProfilerSamplingRateInNanoseconds = 1000000; + + //! Read a list of providers: "Provider[,Provider]" + //! Provider: "(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]" + //! KeyValueArgs: "[key1=value1][;key2=value2]" + static bool TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result); +}; + +#endif // FEATURE_PERFTRACING + +#endif // __EVENTPIPE_PROTOCOL_HELPER_H__ diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h index dffdc7e3db..cf89cf3229 100644 --- a/src/vm/eventpipeprovider.h +++ b/src/vm/eventpipeprovider.h @@ -72,7 +72,7 @@ public: // Create a new event. EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, BYTE *pMetadata = NULL, unsigned int metadataLength = 0); - private: +private: // Create a new event, but allow needStack to be specified. // In general, we want stack walking to be controlled by the consumer and not the producer of events. diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp index 7388472c7d..03bc6c681a 100644 --- a/src/vm/eventpipesession.cpp +++ b/src/vm/eventpipesession.cpp @@ -6,30 +6,30 @@ #include "eventpipe.h" #include "eventpipeprovider.h" #include "eventpipesession.h" +#include "eventpipesessionprovider.h" #ifdef FEATURE_PERFTRACING EventPipeSession::EventPipeSession( EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - unsigned int numProviders, - UINT64 multiFileTraceLengthInSeconds) + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; + PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr)); } CONTRACTL_END; m_sessionType = sessionType; m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB; m_rundownEnabled = false; - m_pProviderList = new EventPipeSessionProviderList( - pProviders, - numProviders); + m_pProviderList = new EventPipeSessionProviderList(pProviders, numProviders); m_multiFileTraceLengthInSeconds = multiFileTraceLengthInSeconds; GetSystemTimeAsFileTime(&m_sessionStartTime); QueryPerformanceCounter(&m_sessionStartTimeStamp); @@ -90,218 +90,4 @@ EventPipeSessionProvider* EventPipeSession::GetSessionProvider(EventPipeProvider return m_pProviderList->GetSessionProvider(pProvider); } -EventPipeSessionProviderList::EventPipeSessionProviderList( - EventPipeProviderConfiguration *pConfigs, - unsigned int numConfigs) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - m_pProviders = new SList<SListElem<EventPipeSessionProvider*>>(); - m_pCatchAllProvider = NULL; - for(unsigned int i=0; i<numConfigs; i++) - { - EventPipeProviderConfiguration *pConfig = &pConfigs[i]; - - // 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<EventPipeSessionProvider*>(pProvider)); - } - } -} - -EventPipeSessionProviderList::~EventPipeSessionProviderList() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(m_pProviders != NULL) - { - SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead(); - while(pElem != NULL) - { - EventPipeSessionProvider *pProvider = pElem->GetValue(); - delete pProvider; - - SListElem<EventPipeSessionProvider*> *pCurElem = pElem; - pElem = m_pProviders->GetNext(pElem); - delete pCurElem; - } - - delete m_pProviders; - m_pProviders = NULL; - } - if(m_pCatchAllProvider != NULL) - { - delete(m_pCatchAllProvider); - m_pCatchAllProvider = NULL; - } -} - -void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if(pProvider != NULL) - { - m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider)); - } -} - -EventPipeSessionProvider* EventPipeSessionProviderList::GetSessionProvider( - EventPipeProvider *pProvider) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config. - if(m_pCatchAllProvider != NULL) - { - return m_pCatchAllProvider; - } - - if(m_pProviders == NULL) - { - return NULL; - } - - SString providerNameStr = pProvider->GetProviderName(); - LPCWSTR providerName = providerNameStr.GetUnicode(); - - EventPipeSessionProvider *pSessionProvider = NULL; - SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead(); - while(pElem != NULL) - { - EventPipeSessionProvider *pCandidate = pElem->GetValue(); - if(wcscmp(providerName, pCandidate->GetProviderName()) == 0) - { - pSessionProvider = pCandidate; - break; - } - pElem = m_pProviders->GetNext(pElem); - } - - return pSessionProvider; -} - -bool EventPipeSessionProviderList::IsEmpty() const -{ - LIMITED_METHOD_CONTRACT; - - return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL); -} - -EventPipeSessionProvider::EventPipeSessionProvider( - LPCWSTR providerName, - UINT64 keywords, - EventPipeEventLevel loggingLevel, - 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; - - // C++ standard, $5.3.5/2: Deleting a NULL pointer is safe. - delete[] m_pProviderName; - m_pProviderName = NULL; - - delete[] m_pFilterData; - m_pFilterData = NULL; -} - -LPCWSTR EventPipeSessionProvider::GetProviderName() const -{ - LIMITED_METHOD_CONTRACT; - return m_pProviderName; -} - -UINT64 EventPipeSessionProvider::GetKeywords() const -{ - LIMITED_METHOD_CONTRACT; - return m_keywords; -} - -EventPipeEventLevel EventPipeSessionProvider::GetLevel() const -{ - LIMITED_METHOD_CONTRACT; - return m_loggingLevel; -} - -LPCWSTR EventPipeSessionProvider::GetFilterData() const -{ - LIMITED_METHOD_CONTRACT; - return m_pFilterData; -} - #endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h index 3c4f293407..0474c7cafe 100644 --- a/src/vm/eventpipesession.h +++ b/src/vm/eventpipesession.h @@ -7,15 +7,14 @@ #ifdef FEATURE_PERFTRACING -enum class EventPipeEventLevel; -struct EventPipeProviderConfiguration; class EventPipeSessionProviderList; class EventPipeSessionProvider; enum class EventPipeSessionType { File, - Streaming + Streaming, + IpcStream }; class EventPipeSession @@ -49,10 +48,9 @@ public: EventPipeSession( EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, - EventPipeProviderConfiguration *pProviders, - unsigned int numProviders, - UINT64 multiFileTraceLengthInSeconds); - + const EventPipeProviderConfiguration *pProviders, + uint32_t numProviders, + uint64_t multiFileTraceLengthInSeconds); ~EventPipeSession(); // Determine if the session is valid or not. Invalid sessions can be detected before they are enabled. @@ -113,68 +111,6 @@ public: EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider); }; -class EventPipeSessionProviderList -{ - -private: - - // The list of providers. - SList<SListElem<EventPipeSessionProvider*>> *m_pProviders; - - // A catch-all provider used when tracing is enabled for all events. - EventPipeSessionProvider *m_pCatchAllProvider; - -public: - - // Create a new list based on the input. - EventPipeSessionProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int 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(EventPipeProvider *pProvider); - - // Returns true if the list is empty. - bool IsEmpty() const; -}; - -class EventPipeSessionProvider -{ -private: - - // The provider name. - WCHAR *m_pProviderName; - - // The enabled keywords. - UINT64 m_keywords; - - // The loging level. - EventPipeEventLevel m_loggingLevel; - - // The filter data. - WCHAR *m_pFilterData; - -public: - - EventPipeSessionProvider( - LPCWSTR providerName, - UINT64 keywords, - EventPipeEventLevel loggingLevel, - LPCWSTR filterData); - ~EventPipeSessionProvider(); - - LPCWSTR GetProviderName() const; - - UINT64 GetKeywords() const; - - EventPipeEventLevel GetLevel() const; - - LPCWSTR GetFilterData() const; -}; - #endif // FEATURE_PERFTRACING #endif // __EVENTPIPE_SESSION_H__ diff --git a/src/vm/eventpipesessionprovider.cpp b/src/vm/eventpipesessionprovider.cpp new file mode 100644 index 0000000000..7e79c47958 --- /dev/null +++ b/src/vm/eventpipesessionprovider.cpp @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "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<SListElem<EventPipeSessionProvider *>>(); + m_pCatchAllProvider = NULL; + + if ((numConfigs > 0) && (pConfigs == nullptr)) + return; // TODO: This seems the logical thing to do here. + + 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<EventPipeSessionProvider *>(pProvider)); + } + } +} + +EventPipeSessionProviderList::~EventPipeSessionProviderList() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (m_pProviders != NULL) + { + SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead(); + while (pElem != NULL) + { + EventPipeSessionProvider *pProvider = pElem->GetValue(); + delete pProvider; + + SListElem<EventPipeSessionProvider *> *pCurElem = pElem; + pElem = m_pProviders->GetNext(pElem); + delete pCurElem; + } + + delete m_pProviders; + } + + 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<EventPipeSessionProvider *>(pProvider)); +} + +EventPipeSessionProvider *EventPipeSessionProviderList::GetSessionProvider( + EventPipeProvider *pProvider) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(pProvider != nullptr); // TODO: This seems like a reasonable pre-condition + } + CONTRACTL_END; + + // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config. + if (m_pCatchAllProvider != NULL) + return m_pCatchAllProvider; + + if (m_pProviders == NULL) + return NULL; + + SString providerNameStr = pProvider->GetProviderName(); + LPCWSTR providerName = providerNameStr.GetUnicode(); + + EventPipeSessionProvider *pSessionProvider = NULL; + SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead(); + while (pElem != NULL) + { + EventPipeSessionProvider *pCandidate = pElem->GetValue(); + if (wcscmp(providerName, pCandidate->GetProviderName()) == 0) + { + pSessionProvider = pCandidate; + break; + } + pElem = m_pProviders->GetNext(pElem); + } + + return pSessionProvider; +} + +bool EventPipeSessionProviderList::IsEmpty() const +{ + LIMITED_METHOD_CONTRACT; + + return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL); +} + +#endif // FEATURE_PERFTRACING diff --git a/src/vm/eventpipesessionprovider.h b/src/vm/eventpipesessionprovider.h new file mode 100644 index 0000000000..7b500e4412 --- /dev/null +++ b/src/vm/eventpipesessionprovider.h @@ -0,0 +1,81 @@ +#ifndef __EVENTPIPE_SESSION_PROVIDER_SESSION_H__ +#define __EVENTPIPE_SESSION_PROVIDER_SESSION_H__ + +#ifdef FEATURE_PERFTRACING + +enum class EventPipeEventLevel; +class EventPipeProvider; + +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(EventPipeProvider *pProvider); + + // Returns true if the list is empty. + bool IsEmpty() const; + + EventPipeSessionProviderList() = delete; + EventPipeSessionProviderList(const EventPipeSessionProviderList &other) = delete; + EventPipeSessionProviderList(EventPipeSessionProviderList &&other) = delete; + EventPipeSessionProviderList &operator=(const EventPipeSessionProviderList &rhs) = delete; + EventPipeSessionProviderList &&operator=(EventPipeSessionProviderList &&rhs) = delete; + +private: + SList<SListElem<EventPipeSessionProvider*>> *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/vm/fastserializer.cpp b/src/vm/fastserializer.cpp index 7a79b2ff19..89dbe50728 100644 --- a/src/vm/fastserializer.cpp +++ b/src/vm/fastserializer.cpp @@ -11,7 +11,47 @@ // 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 -FastSerializer::FastSerializer(SString &outputFilePath) +IpcStreamWriter::IpcStreamWriter(IpcStream *pStream) : _pStream(pStream) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(_pStream != nullptr); + } + CONTRACTL_END; +} + +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; + + return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten); +} + +FileStreamWriter::FileStreamWriter(const SString &outputFilePath) { CONTRACTL { @@ -21,21 +61,17 @@ FastSerializer::FastSerializer(SString &outputFilePath) } CONTRACTL_END; - m_writeErrorEncountered = false; - m_currentPos = 0; m_pFileStream = new CFileStream(); - if(FAILED(m_pFileStream->OpenForWrite(outputFilePath))) + if (FAILED(m_pFileStream->OpenForWrite(outputFilePath))) { _ASSERTE(!"Unable to open file for write."); - delete(m_pFileStream); + delete m_pFileStream; m_pFileStream = NULL; return; } - - WriteFileHeader(); } -FastSerializer::~FastSerializer() +FileStreamWriter::~FileStreamWriter() { CONTRACTL { @@ -45,18 +81,54 @@ FastSerializer::~FastSerializer() } CONTRACTL_END; - if(m_pFileStream != NULL) + delete m_pFileStream; +} + +bool FileStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const +{ + CONTRACTL { - delete(m_pFileStream); - m_pFileStream = NULL; + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + PRECONDITION(lpBuffer != nullptr); + PRECONDITION(nBytesToWrite > 0); + } + CONTRACTL_END; + + ULONG outCount; + HRESULT hResult = m_pFileStream->Write(lpBuffer, nBytesToWrite, &outCount); + nBytesWritten = static_cast<uint32_t>(outCount); + return hResult == S_OK; +} + +FastSerializer::FastSerializer(StreamWriter *pStreamWriter) : m_pStreamWriter(pStreamWriter) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(m_pStreamWriter != NULL); } + CONTRACTL_END; + + m_writeErrorEncountered = false; + m_currentPos = 0; + WriteFileHeader(); } -StreamLabel FastSerializer::GetStreamLabel() const +FastSerializer::~FastSerializer() { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; - return (StreamLabel)m_currentPos; + delete m_pStreamWriter; } void FastSerializer::WriteObject(FastSerializableObject *pObject) @@ -92,15 +164,13 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length) } CONTRACTL_END; - if(m_writeErrorEncountered || m_pFileStream == NULL) - { + if (m_writeErrorEncountered || m_pStreamWriter == NULL) return; - } EX_TRY { - ULONG outCount; - m_pFileStream->Write(pBuffer, length, &outCount); + uint32_t outCount; + m_pStreamWriter->Write(pBuffer, length, outCount); #ifdef _DEBUG size_t prevPos = m_currentPos; @@ -120,7 +190,7 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length) EX_CATCH { m_writeErrorEncountered = true; - } + } EX_END_CATCH(SwallowAllExceptions); } @@ -142,10 +212,10 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject) WriteTag(FastSerializerTags::NullReference); // Write the SerializationType version fields. - int serializationType[2]; - serializationType[0] = pObject->GetObjectVersion(); - serializationType[1] = pObject->GetMinReaderVersion(); - WriteBuffer((BYTE*) &serializationType, sizeof(serializationType)); + int serializationType[2] = { + pObject->GetObjectVersion(), + pObject->GetMinReaderVersion()}; + WriteBuffer((BYTE *)&serializationType, sizeof(serializationType)); // Write the SerializationType TypeName field. const char *strTypeName = pObject->GetTypeName(); @@ -157,7 +227,6 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject) WriteTag(FastSerializerTags::EndObject); } - void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength) { CONTRACTL @@ -169,7 +238,7 @@ void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned in CONTRACTL_END; WriteBuffer((BYTE *)&tag, sizeof(tag)); - if(payload != NULL) + if (payload != NULL) { _ASSERTE(payloadLength > 0); WriteBuffer(payload, payloadLength); @@ -202,10 +271,10 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length) CONTRACTL_END; // Write the string length . - WriteBuffer((BYTE*) &length, sizeof(length)); + WriteBuffer((BYTE *)&length, sizeof(length)); // Write the string contents. - WriteBuffer((BYTE*) strContents, length); + WriteBuffer((BYTE *)strContents, length); } #endif // FEATURE_PERFTRACING diff --git a/src/vm/fastserializer.h b/src/vm/fastserializer.h index fad04de48c..3b4de65bf5 100644 --- a/src/vm/fastserializer.h +++ b/src/vm/fastserializer.h @@ -11,23 +11,20 @@ #include "fastserializableobject.h" #include "fstream.h" - -class FastSerializer; - -typedef unsigned int StreamLabel; +#include "diagnosticsipc.h" // 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 : BYTE +enum class FastSerializerTags : BYTE { - 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 + 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 + 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, @@ -35,17 +32,53 @@ enum class FastSerializerTags : BYTE SkipRegion, String, Blob, - Limit // Just past the last valid tag, used for asserts. + Limit // Just past the last valid tag, used for asserts. }; -class FastSerializer +//! +//! 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; +}; - FastSerializer(SString &outputFilePath); - ~FastSerializer(); +//! +//! Implements a StreamWriter for writing bytes to an IPC. +//! +class IpcStreamWriter final : public StreamWriter +{ +public: + IpcStreamWriter(IpcStream *pStream); + ~IpcStreamWriter(); + bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const; + +private: + IpcStream *const _pStream; +}; + +//! +//! Implements a StreamWriter for writing bytes to an 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; +}; - StreamLabel GetStreamLabel() const; +class FastSerializer +{ +public: + FastSerializer(StreamWriter *pStreamWriter); + ~FastSerializer(); void WriteObject(FastSerializableObject *pObject); void WriteBuffer(BYTE *pBuffer, unsigned int length); @@ -55,16 +88,14 @@ public: size_t GetCurrentPosition() const { LIMITED_METHOD_CONTRACT; - return m_currentPos; } private: - void WriteSerializationType(FastSerializableObject *pObject); void WriteFileHeader(); - CFileStream *m_pFileStream; + StreamWriter *const m_pStreamWriter; bool m_writeErrorEncountered; size_t m_currentPos; }; diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp index faec6e5c97..10ed4944b6 100644 --- a/src/vm/mscorlib.cpp +++ b/src/vm/mscorlib.cpp @@ -84,10 +84,12 @@ #if defined(FEATURE_EVENTSOURCE_XPLAT) #include "nativeeventsource.h" #include "eventpipe.h" +#include "eventpipeinternal.h" #endif //defined(FEATURE_EVENTSOURCE_XPLAT) #ifdef FEATURE_PERFTRACING #include "eventpipe.h" +#include "eventpipeinternal.h" #endif //FEATURE_PERFTRACING #endif // CROSSGEN_MSCORLIB |