This is the first commit to enable a "diagnostic port" using IPC (Named Pipe on Windows and Unix Domain Socket on other platforms). This change currently enable EventPipe to be enabled/disabled without the use of a file drop.
- Split the DiagnosticsIpc into (DiagnosticsIpc/IpcStream)
- DiagnosticsIpc (IPC listener) is meant to be used by the Diagnostic server.
- IpcStream (IPC channel) is meant to be use to communicate with the connected client.
- Change the FastSerializer dependency from `CFileStream` to `StreamWriter`
This abstraction is meant decouple the writing of objects in order to extend its usability.
The main objective is to reuse FastSerializer to stream data through the open IPC channel.
- Moved the EventPipeSessionProvider* classes to their own file.
- Added a more streamlined parsing achievable by defining a simpler binary protocol (by noahfalk).
1. Only one allocation is needed for the EventPipeProviderConfiguration array, no allocations or copies are needed for strings because we can refer to them directly out of the incoming command buffer
2. No change to the EventPipe API for enable is required. EventPipeProviderConfiguration retains its current behavior of not deleting the string pointers it holds.
3. No leaks happen because the command buffer owns the string memory and ensures that it stays alive for the duration of the Enable() call.
s_sessionID = EventPipeInternal.Enable(
configuration.OutputFile,
configuration.CircularBufferSizeInMB,
- configuration.ProfilerSamplingRateInNanoseconds,
+ (ulong)configuration.ProfilerSamplingRateInNanoseconds,
providers,
- providers.Length,
+ (uint)providers.Length,
configuration.MultiFileTraceLengthInSeconds);
}
// 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);
include_directories(../../inc) #needed for warning control
set(TWO_WAY_PIPE_SOURCES
+ win/diagnosticsipc.cpp
win/twowaypipe.cpp
win/processdescriptor.cpp
)
add_definitions(-D_POSIX_C_SOURCE=200809L)
set(TWO_WAY_PIPE_SOURCES
+ unix/diagnosticsipc.cpp
unix/twowaypipe.cpp
unix/processdescriptor.cpp
)
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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__
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)); \
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,
#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
{
/*++
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)
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");
}
// 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;
}
}
#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);
}
/*++
comwaithandle.cpp
customattribute.cpp
custommarshalerinfo.cpp
+ diagnosticserver.cpp
dllimportcallback.cpp
eeconfig.cpp
eecontract.cpp
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
comwaithandle.h
customattribute.h
custommarshalerinfo.h
+ diagnosticserver.h
+ diagnosticsprotocol.h
dllimportcallback.h
eeconfig.h
eecontract.h
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
#include "perfmap.h"
#endif
+#include "diagnosticserver.h"
#include "eventpipe.h"
#ifndef FEATURE_PAL
#ifdef FEATURE_PERFTRACING
// Initialize the event pipe.
+ DiagnosticServer::Initialize();
EventPipe::Initialize();
#endif // FEATURE_PERFTRACING
#ifdef FEATURE_PERFTRACING
// Shutdown the event pipe.
EventPipe::Shutdown();
+ DiagnosticServer::Shutdown();
#endif // FEATURE_PERFTRACING
#if defined(FEATURE_COMINTEROP)
#include <stdlib.h>
#include <wchar.h>
#include <objbase.h>
-#include <stddef.h>
#include <float.h>
#include <math.h>
#include <time.h>
--- /dev/null
+// 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
--- /dev/null
+// 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__
--- /dev/null
+// 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__
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
#include "eventpipeconfiguration.h"
+#include "eventpipesessionprovider.h"
#include "eventpipeevent.h"
#include "eventpipeeventsource.h"
#include "eventpipefile.h"
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;
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);
}
}
CONTRACTL_END;
- if(m_allocatedData && m_pData != NULL)
+ if (m_allocatedData && m_pData != NULL)
{
delete[] m_pData;
m_pData = NULL;
}
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;
}
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
{
{
Disable((EventPipeSessionID)s_pSession);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
// Save pointers to the configuration and buffer manager.
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.
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;
SampleProfiler::Enable();
// Enable the file switch timer if needed.
- if(s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
+ if (s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
{
CreateFileSwitchTimer();
}
// 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;
}
// 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();
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);
s_pSession = NULL;
}
- delete(s_pFile);
+ delete s_pFile;
s_pFile = NULL;
}
}
CONTRACTL_END
- NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext();
+ NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
if (timerContextHolder == NULL)
{
return;
}
CONTRACTL_END
- if((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
+ if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
{
s_fileSwitchTimerHandle = NULL;
}
}
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()
// 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;
// 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);
}
}
}
-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;
}
LIMITED_METHOD_CONTRACT;
bool enabled = false;
- if(s_pConfig != NULL)
+ if (s_pConfig != NULL)
{
enabled = s_pConfig->Enabled();
}
return enabled;
}
-EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
}
return pProvider;
-
}
-EventPipeProvider* EventPipe::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
{
CONTRACTL
{
// 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();
CONTRACTL_END;
// Exit early if the event is not enabled.
- if(!event.IsEnabled())
+ if (!event.IsEnabled())
{
return;
}
// Get the current thread;
Thread *pThread = GetThread();
- if(s_pConfig == NULL)
+ if (s_pConfig == NULL)
{
// We can't procede without a configuration
return;
_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;
}
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
{
s_pFile->WriteEvent(instance);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
}
}
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.
CONTRACTL_END;
Thread *pThread = GetThread();
- if(pThread != NULL)
+ if (pThread != NULL)
{
return WalkManagedStackForThread(pThread, stackContents);
}
// 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);
// 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;
// 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;
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]);
#endif
}
-EventPipeEventInstance* EventPipe::GetNextEvent()
+EventPipeEventInstance *EventPipe::GetNextEvent()
{
CONTRACTL
{
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
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeFile;
-class EventPipeJsonFile;
-class EventPipeBuffer;
class EventPipeBufferManager;
class EventPipeEventSource;
class EventPipeProvider;
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.
struct EventData
{
-public:
UINT64 Ptr;
unsigned int Size;
unsigned int Reserved;
// 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
return m_size;
}
- EventData* GetEventDataArray() const
+ EventData *GetEventDataArray() const
{
LIMITED_METHOD_CONTRACT;
class StackContents
{
private:
-
const static unsigned int MAX_STACK_DEPTH = 100;
// Array of IP values from a stack crawl.
#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;
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;
}
}
#ifdef _DEBUG
- MethodDesc* GetMethod(unsigned int frameIndex)
+ MethodDesc *GetMethod(unsigned int frameIndex)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(frameIndex < MAX_STACK_DEPTH);
{
LIMITED_METHOD_CONTRACT;
- if(m_nextAvailableFrame < MAX_STACK_DEPTH)
+ if (m_nextAvailableFrame < MAX_STACK_DEPTH)
{
m_stackFrames[m_nextAvailableFrame] = controlPC;
#ifdef _DEBUG
}
}
- BYTE* GetPointer() const
+ BYTE *GetPointer() const
{
LIMITED_METHOD_CONTRACT;
- return (BYTE*)m_stackFrames;
+ return (BYTE *)m_stackFrames;
}
unsigned int GetSize() const
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
}
};
-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__
#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()
{
m_pRundownThread = NULL;
m_pConfigProvider = NULL;
m_pSession = NULL;
- m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
+ m_pProviderList = new SList<SListElem<EventPipeProvider *>>();
}
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
// 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;
// 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
{
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
// See if we've already registered this provider.
EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName());
- if(pExistingProvider != NULL)
+ if (pExistingProvider != NULL)
{
return false;
}
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 */,
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;
}
}
// 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;
}
}
return false;
}
-EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProvider(const SString &providerName)
{
CONTRACTL
{
return GetProviderNoLock(providerName);
}
-EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
{
CONTRACTL
{
// 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;
}
return NULL;
}
-EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
+EventPipeSessionProvider *EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
{
CONTRACTL
{
CONTRACTL_END;
EventPipeSessionProvider *pRet = NULL;
- if(pSession != NULL)
+ if (pSession != NULL)
{
- pRet = pSession->GetSessionProvider(pProvider);
+ pRet = pSession->GetSessionProvider(pProvider);
}
return pRet;
}
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)
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);
}
}
// 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 */,
// 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(
Enable(pSession);
}
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
+EventPipeEventInstance *EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
{
CONTRACTL
{
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.
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);
}
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeProvider;
-struct EventPipeProviderConfiguration;
class EventPipeSession;
enum class EventPipeSessionType;
-class EventPipeSessionProvider;
enum class EventPipeEventLevel
{
class EventPipeConfiguration
{
public:
-
EventPipeConfiguration();
~EventPipeConfiguration();
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);
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);
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();
}
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;
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;
// 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;
#include "fastserializableobject.h"
#include "fastserializer.h"
+class EventPipeJsonFile;
+
class EventPipeEventInstance
{
// Declare friends.
#include "eventpipemetadatagenerator.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
#ifdef FEATURE_PERFTRACING
#ifdef FEATURE_PERFTRACING
-EventPipeFile::EventPipeFile(
- SString &outputFilePath)
+EventPipeFile::EventPipeFile(SString &outputFilePath)
{
CONTRACTL
{
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>();
metadataId = GenerateMetadataId();
EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId);
-
+
WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event.
SaveMetadataId(*instance.GetEvent(), metadataId);
// "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)
--- /dev/null
+// 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
--- /dev/null
+// 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__
--- /dev/null
+// 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
--- /dev/null
+// 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__
// 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.
#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);
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
#ifdef FEATURE_PERFTRACING
-enum class EventPipeEventLevel;
-struct EventPipeProviderConfiguration;
class EventPipeSessionProviderList;
class EventPipeSessionProvider;
enum class EventPipeSessionType
{
File,
- Streaming
+ Streaming,
+ IpcStream
};
class EventPipeSession
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.
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__
--- /dev/null
+// 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
--- /dev/null
+#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__
// 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
{
}
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
{
}
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)
}
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;
EX_CATCH
{
m_writeErrorEncountered = true;
- }
+ }
EX_END_CATCH(SwallowAllExceptions);
}
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();
WriteTag(FastSerializerTags::EndObject);
}
-
void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength)
{
CONTRACTL
CONTRACTL_END;
WriteBuffer((BYTE *)&tag, sizeof(tag));
- if(payload != NULL)
+ if (payload != NULL)
{
_ASSERTE(payloadLength > 0);
WriteBuffer(payload, payloadLength);
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
#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,
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);
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;
};
#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