summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Rivero <jorive@microsoft.com>2019-04-02 07:06:42 -0700
committerGitHub <noreply@github.com>2019-04-02 07:06:42 -0700
commit40dda195590873d18236bfbc7742bb6fe7305730 (patch)
tree4c45036684f5c4c2cf6d38f19a95626ac5fc2082
parentd6dad7520ea0bac3894e908835d0321075df822d (diff)
downloadcoreclr-40dda195590873d18236bfbc7742bb6fe7305730.tar.gz
coreclr-40dda195590873d18236bfbc7742bb6fe7305730.tar.bz2
coreclr-40dda195590873d18236bfbc7742bb6fe7305730.zip
[EventPipe] Streaming events out-of-proc using IPC (#23448)
- There was a race condition on EventPipe::Disable where we deallocated the s_pSession and set it to NULL, but there was still a thread waiting to write and dereference the null pointer. Now, we check that the session is not null. - Added a new attach event to be handled to the Diagnostic server: Add streaming functionality (Syncronus) #23299 - On Linux, IPC was failing when delete was invoked because we were using new (std::nothrow) instead of new (nothrow) when allocating the newly connected clients/streams. I have replaced the call with new. This fixed #23580 - Move multiFileTraceLengthInSeconds out of the EventPipeSession. - Unlink previously existing socket. - EventPipeBlock: _ASSERTE was updated not to fail when data is not aligned if an error has already occurred. - Update _ASSERTE in fastserializer.cpp to take into account the the write operation could have failed.
-rw-r--r--src/debug/debug-pal/unix/diagnosticsipc.cpp10
-rw-r--r--src/debug/debug-pal/win/diagnosticsipc.cpp12
-rw-r--r--src/vm/diagnosticserver.cpp67
-rw-r--r--src/vm/diagnosticserver.h14
-rw-r--r--src/vm/eventpipe.cpp266
-rw-r--r--src/vm/eventpipe.h58
-rw-r--r--src/vm/eventpipeblock.cpp5
-rw-r--r--src/vm/eventpipeblock.h115
-rw-r--r--src/vm/eventpipebuffermanager.cpp6
-rw-r--r--src/vm/eventpipebuffermanager.h4
-rw-r--r--src/vm/eventpipeconfiguration.cpp6
-rw-r--r--src/vm/eventpipeconfiguration.h3
-rw-r--r--src/vm/eventpipefile.cpp32
-rw-r--r--src/vm/eventpipefile.h112
-rw-r--r--src/vm/eventpipeprotocolhelper.cpp89
-rw-r--r--src/vm/eventpipeprotocolhelper.h12
-rw-r--r--src/vm/eventpipesession.cpp4
-rw-r--r--src/vm/eventpipesession.h12
-rw-r--r--src/vm/fastserializableobject.h32
-rw-r--r--src/vm/fastserializer.cpp20
-rw-r--r--src/vm/fastserializer.h6
21 files changed, 499 insertions, 386 deletions
diff --git a/src/debug/debug-pal/unix/diagnosticsipc.cpp b/src/debug/debug-pal/unix/diagnosticsipc.cpp
index d9e0e94c9f..fafb06c56b 100644
--- a/src/debug/debug-pal/unix/diagnosticsipc.cpp
+++ b/src/debug/debug-pal/unix/diagnosticsipc.cpp
@@ -4,7 +4,6 @@
#include <pal.h>
#include <pal_assert.h>
-#include <new>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
@@ -14,7 +13,7 @@
IpcStream::DiagnosticsIpc::DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress) :
_serverSocket(serverSocket),
- _pServerAddress(new (std::nothrow) sockaddr_un),
+ _pServerAddress(new sockaddr_un),
_isUnlinked(false)
{
_ASSERTE(_pServerAddress != nullptr);
@@ -105,10 +104,7 @@ IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
return nullptr;
}
- auto pIpcStream = new (std::nothrow) IpcStream(clientSocket);
- if (pIpcStream == nullptr && callback != nullptr)
- callback("Failed to allocate an IpcStream object.", 1);
- return pIpcStream;
+ return new IpcStream(clientSocket);
}
//! This helps remove the socket from the filesystem when the runtime exits.
@@ -137,6 +133,8 @@ IpcStream::~IpcStream()
{
if (_clientSocket != -1)
{
+ Flush();
+
const int fSuccessClose = ::close(_clientSocket);
_ASSERTE(fSuccessClose != -1);
}
diff --git a/src/debug/debug-pal/win/diagnosticsipc.cpp b/src/debug/debug-pal/win/diagnosticsipc.cpp
index 46f212836b..5467581f4a 100644
--- a/src/debug/debug-pal/win/diagnosticsipc.cpp
+++ b/src/debug/debug-pal/win/diagnosticsipc.cpp
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
#include <assert.h>
-#include <new>
#include <stdio.h>
#include "diagnosticsipc.h"
@@ -47,7 +46,7 @@ IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
const uint32_t nOutBufferSize = 16 * 1024;
HANDLE hPipe = ::CreateNamedPipeA(
_pNamedPipeName, // pipe name
- PIPE_ACCESS_DUPLEX/* | FILE_FLAG_OVERLAPPED*/, // read/write access
+ PIPE_ACCESS_DUPLEX, // 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
@@ -71,10 +70,7 @@ IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
return nullptr;
}
- auto pIpcStream = new (std::nothrow) IpcStream(hPipe);
- if (pIpcStream == nullptr && callback != nullptr)
- callback("Failed to allocate an IpcStream object.", 1);
- return pIpcStream;
+ return new IpcStream(hPipe);
}
void IpcStream::DiagnosticsIpc::Unlink(ErrorCallback callback)
@@ -85,11 +81,13 @@ IpcStream::~IpcStream()
{
if (_hPipe != INVALID_HANDLE_VALUE)
{
+ Flush();
+
const BOOL fSuccessDisconnectNamedPipe = ::DisconnectNamedPipe(_hPipe);
assert(fSuccessDisconnectNamedPipe != 0);
const BOOL fSuccessCloseHandle = ::CloseHandle(_hPipe);
- assert(CloseHandle != 0);
+ assert(fSuccessCloseHandle != 0);
}
}
diff --git a/src/vm/diagnosticserver.cpp b/src/vm/diagnosticserver.cpp
index 3a700383ac..81cdc25d5b 100644
--- a/src/vm/diagnosticserver.cpp
+++ b/src/vm/diagnosticserver.cpp
@@ -18,8 +18,7 @@ static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter)
{
CONTRACTL
{
- // TODO: Maybe this should not throw.
- THROWS;
+ NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(lpThreadParameter != nullptr);
@@ -37,39 +36,51 @@ static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter)
STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, "warning (%d): %s.\n", code, szMessage);
};
- while (true)
+ EX_TRY
{
- // 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))
+ while (true)
{
- delete pStream;
- continue;
- }
+ // 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;
+ switch (header.RequestType)
+ {
+ case DiagnosticMessageType::EnableEventPipe:
+ EventPipeProtocolHelper::EnableFileTracingEventHandler(pStream);
+ break;
- case DiagnosticMessageType::DisableEventPipe:
- EventPipeProtocolHelper::DisableTracingEventHandler(pStream);
- break;
+ case DiagnosticMessageType::DisableEventPipe:
+ EventPipeProtocolHelper::DisableFileTracingEventHandler(pStream);
+ break;
- default:
- LOG((LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType));
- break;
+ case DiagnosticMessageType::StreamEventPipe:
+ EventPipeProtocolHelper::AttachTracingEventHandler(pStream);
+ break;
+
+ default:
+ LOG((LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType));
+ break;
+ }
}
}
+ EX_CATCH
+ {
+ STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Exception caught in diagnostic thread. Leaving thread now.\n");
+ _ASSERTE(!"Hit an error in the diagnostic server thread\n.");
+ }
+ EX_END_CATCH(SwallowAllExceptions);
return 0;
}
diff --git a/src/vm/diagnosticserver.h b/src/vm/diagnosticserver.h
index 51f32ae09f..76ad3eff23 100644
--- a/src/vm/diagnosticserver.h
+++ b/src/vm/diagnosticserver.h
@@ -20,17 +20,9 @@ enum class DiagnosticMessageType : uint32_t
// 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,
+ StreamEventPipe,
+ AttachEventPipe,
+ DetachEventPipe,
///////////////////////////////////////////////////////////////////////////
// Profiler = 2048
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 634f1d0d69..92b4793f2e 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -5,6 +5,7 @@
#include "common.h"
#include "clrtypes.h"
#include "safemath.h"
+#include "diagnosticsipc.h"
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
#include "eventpipeconfiguration.h"
@@ -36,7 +37,8 @@ EventPipeEventSource *EventPipe::s_pEventSource = NULL;
LPCWSTR EventPipe::s_pCommandLine = NULL;
unsigned long EventPipe::s_nextFileIndex;
HANDLE EventPipe::s_fileSwitchTimerHandle = NULL;
-ULONGLONG EventPipe::s_lastFileSwitchTime = 0;
+ULONGLONG EventPipe::s_lastFlushSwitchTime = 0;
+uint64_t EventPipe::s_multiFileTraceLengthInSeconds = 0;
#ifdef FEATURE_PAL
// This function is auto-generated from /src/scripts/genEventPipe.py
@@ -45,24 +47,6 @@ extern "C" void InitProvidersAndEvents();
void InitProvidersAndEvents();
#endif
-EventPipeEventPayload::EventPipeEventPayload(BYTE *pData, unsigned int length)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- m_pData = pData;
- m_pEventData = NULL;
- m_eventDataCount = 0;
- m_allocatedData = false;
-
- m_size = length;
-}
-
EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount)
{
CONTRACTL
@@ -272,33 +256,118 @@ EventPipeSessionID EventPipe::Enable(
// Take the lock before enabling tracing.
CrstHolder _crst(GetLock());
+ s_multiFileTraceLengthInSeconds = multiFileTraceLengthInSeconds;
+
// Create a new session.
SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
EventPipeSession *pSession = s_pConfig->CreateSession(
- (strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
+ (strOutputPath != nullptr) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
circularBufferSizeInMB,
pProviders,
- numProviders,
- multiFileTraceLengthInSeconds);
+ numProviders);
+
+ // Initialize the next file index.
+ s_nextFileIndex = 1;
+
+ // Initialize the last file switch time.
+ s_lastFlushSwitchTime = CLRGetTickCount64();
+
+ // Create the event pipe file.
+ // A NULL output path means that we should not write the results to a file.
+ // This is used in the EventListener streaming case.
+ if (strOutputPath != NULL)
+ {
+
+ // Save the output file path.
+ SString outputPath(strOutputPath);
+ SIZE_T outputPathLen = outputPath.GetCount();
+ WCHAR *pOutputPath = new WCHAR[outputPathLen + 1];
+ wcsncpy(pOutputPath, outputPath.GetUnicode(), outputPathLen);
+ pOutputPath[outputPathLen] = '\0';
+ s_pOutputPath = pOutputPath;
+
+ SString nextTraceFilePath;
+ GetNextFilePath(nextTraceFilePath);
+
+ s_pFile = new EventPipeFile(new FileStreamWriter(nextTraceFilePath));
+ }
+
+ const DWORD FileSwitchTimerPeriodMS = 1000;
+ return Enable(pSession, SwitchToNextFileTimerCallback, FileSwitchTimerPeriodMS, FileSwitchTimerPeriodMS);
+}
+
+EventPipeSessionID EventPipe::Enable(
+ IpcStream *pStream,
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
+ }
+ CONTRACTL_END;
+
+ if (numProviders == 0 || pProviders == nullptr)
+ return (EventPipeSessionID) nullptr;
+
+ // Take the lock before enabling tracing.
+ CrstHolder _crst(GetLock());
+
+ // Create a new session.
+ SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
+ EventPipeSession *pSession = s_pConfig->CreateSession(
+ EventPipeSessionType::IpcStream,
+ circularBufferSizeInMB,
+ pProviders,
+ numProviders);
+
+ // Reply back to client with the SessionId
+ uint32_t nBytesWritten = 0;
+ EventPipeSessionID sessionId = (EventPipeSessionID) pSession;
+ bool fSuccess = pStream->Write(&sessionId, sizeof(sessionId), nBytesWritten);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ s_pConfig->DeleteSession(pSession);
+
+ delete pStream;
+ return (EventPipeSessionID) nullptr;
+ }
+
+ s_pFile = new EventPipeFile(new IpcStreamWriter(pStream));
// Enable the session.
- return Enable(strOutputPath, pSession);
+ const DWORD FlushTimerPeriodMS = 100; // TODO: Define a good number here for streaming.
+ return Enable(pSession, FlushTimer, FlushTimerPeriodMS, FlushTimerPeriodMS);
}
-EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pSession)
+EventPipeSessionID EventPipe::Enable(
+ EventPipeSession *const pSession,
+ WAITORTIMERCALLBACK callback,
+ DWORD dueTime,
+ DWORD period)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
- PRECONDITION(pSession != NULL);
+ PRECONDITION(pSession != nullptr);
+ PRECONDITION(callback != nullptr);
+ PRECONDITION(dueTime > 0);
+ PRECONDITION(period > 0);
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 == nullptr || s_pConfig->Enabled())
return 0;
// If the state or arguments are invalid, bail here.
@@ -308,31 +377,6 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
// Enable the EventPipe EventSource.
s_pEventSource->Enable(pSession);
- // Initialize the next file index.
- s_nextFileIndex = 1;
-
- // Initialize the last file switch time.
- s_lastFileSwitchTime = CLRGetTickCount64();
-
- // Create the event pipe file.
- // A NULL output path means that we should not write the results to a file.
- // This is used in the EventListener streaming case.
- if (strOutputPath != NULL)
- {
- // Save the output file path.
- SString outputPath(strOutputPath);
- SIZE_T outputPathLen = outputPath.GetCount();
- WCHAR *pOutputPath = new WCHAR[outputPathLen + 1];
- wcsncpy(pOutputPath, outputPath.GetUnicode(), outputPathLen);
- pOutputPath[outputPathLen] = '\0';
- s_pOutputPath = pOutputPath;
-
- SString nextTraceFilePath;
- GetNextFilePath(pSession, nextTraceFilePath);
-
- s_pFile = new EventPipeFile(nextTraceFilePath);
- }
-
// Save the session.
s_pSession = pSession;
@@ -342,11 +386,7 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
// Enable the sample profiler
SampleProfiler::Enable();
- // Enable the file switch timer if needed.
- if (s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
- {
- CreateFileSwitchTimer();
- }
+ CreateFlushTimerCallback(callback, dueTime, period);
// Return the session ID.
return (EventPipeSessionID)s_pSession;
@@ -365,9 +405,7 @@ void EventPipe::Disable(EventPipeSessionID id)
// Only perform the disable operation if the session ID
// matches the current active session.
if (id != (EventPipeSessionID)s_pSession)
- {
return;
- }
// Don't block GC during clean-up.
GCX_PREEMP();
@@ -389,18 +427,20 @@ void EventPipe::Disable(EventPipeSessionID id)
// Disable tracing.
s_pConfig->Disable(s_pSession);
+ s_multiFileTraceLengthInSeconds = 0;
+
// Delete the session.
s_pConfig->DeleteSession(s_pSession);
s_pSession = NULL;
// Delete the file switch timer.
- DeleteFileSwitchTimer();
+ DeleteFlushTimerCallback();
// Flush all write buffers to make sure that all threads see the change.
FlushProcessWriteBuffers();
// Write to the file.
- if (s_pFile != NULL)
+ if (s_pFile != nullptr)
{
LARGE_INTEGER disableTimeStamp;
QueryPerformanceCounter(&disableTimeStamp);
@@ -435,7 +475,7 @@ void EventPipe::Disable(EventPipeSessionID id)
}
delete s_pFile;
- s_pFile = NULL;
+ s_pFile = nullptr;
}
// De-allocate buffers.
@@ -447,22 +487,27 @@ void EventPipe::Disable(EventPipeSessionID id)
}
}
-void EventPipe::CreateFileSwitchTimer()
+void EventPipe::CreateFlushTimerCallback(WAITORTIMERCALLBACK callback, DWORD dueTime, DWORD period)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ PRECONDITION(callback != nullptr);
+ PRECONDITION(dueTime > 0);
+ PRECONDITION(period > 0);
PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END
+ if (s_pFile == nullptr)
+ return;
+
NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
if (timerContextHolder == NULL)
- {
return;
- }
+
timerContextHolder->TimerId = 0;
bool success = false;
@@ -471,10 +516,10 @@ void EventPipe::CreateFileSwitchTimer()
{
if (ThreadpoolMgr::CreateTimerQueueTimer(
&s_fileSwitchTimerHandle,
- SwitchToNextFileTimerCallback,
+ callback,
timerContextHolder,
- FileSwitchTimerPeriodMS,
- FileSwitchTimerPeriodMS,
+ dueTime,
+ period,
0 /* flags */))
{
_ASSERTE(s_fileSwitchTimerHandle != NULL);
@@ -485,6 +530,7 @@ void EventPipe::CreateFileSwitchTimer()
{
}
EX_END_CATCH(RethrowTerminalExceptions);
+
if (!success)
{
_ASSERTE(s_fileSwitchTimerHandle == NULL);
@@ -494,7 +540,7 @@ void EventPipe::CreateFileSwitchTimer()
timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure
}
-void EventPipe::DeleteFileSwitchTimer()
+void EventPipe::DeleteFlushTimerCallback()
{
CONTRACTL
{
@@ -506,9 +552,7 @@ void EventPipe::DeleteFileSwitchTimer()
CONTRACTL_END
if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
- {
s_fileSwitchTimerHandle = NULL;
- }
}
void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired)
@@ -525,19 +569,55 @@ void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN ti
// Take the lock control lock to make sure that tracing isn't disabled during this operation.
CrstHolder _crst(GetLock());
+ if (s_pSession == nullptr || s_pFile == nullptr)
+ return;
+
// 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 || s_multiFileTraceLengthInSeconds == 0)
return;
- }
GCX_PREEMP();
- if (CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000)))
+ if (CLRGetTickCount64() > (s_lastFlushSwitchTime + (s_multiFileTraceLengthInSeconds * 1000)))
{
SwitchToNextFile();
- s_lastFileSwitchTime = CLRGetTickCount64();
+ s_lastFlushSwitchTime = CLRGetTickCount64();
+ }
+}
+
+void WINAPI EventPipe::FlushTimer(PVOID parameter, BOOLEAN timerFired)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(timerFired);
+ }
+ CONTRACTL_END;
+
+ // Take the lock control lock to make sure that tracing isn't disabled during this operation.
+ CrstHolder _crst(GetLock());
+
+ if (s_pSession == nullptr || s_pFile == nullptr)
+ return;
+
+ // Make sure that we should actually switch files.
+ if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::IpcStream)
+ return;
+
+ GCX_PREEMP();
+
+ if (CLRGetTickCount64() > (s_lastFlushSwitchTime + 100))
+ {
+ // Get the current time stamp.
+ // WriteAllBuffersToFile will use this to ensure that no events after
+ // the current timestamp are written into the file.
+ LARGE_INTEGER stopTimeStamp;
+ QueryPerformanceCounter(&stopTimeStamp);
+ s_pBufferManager->WriteAllBuffersToFile(s_pFile, stopTimeStamp);
+
+ s_lastFlushSwitchTime = CLRGetTickCount64();
}
}
@@ -548,7 +628,7 @@ void EventPipe::SwitchToNextFile()
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
- PRECONDITION(s_pSession != NULL);
+ PRECONDITION(s_pFile != nullptr);
PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END
@@ -561,8 +641,16 @@ void EventPipe::SwitchToNextFile()
// Open the new file.
SString nextTraceFilePath;
- GetNextFilePath(s_pSession, nextTraceFilePath);
- EventPipeFile *pFile = new (nothrow) EventPipeFile(nextTraceFilePath);
+ GetNextFilePath(nextTraceFilePath);
+
+ StreamWriter *pStreamWriter = new (nothrow) FileStreamWriter(nextTraceFilePath);
+ if (pStreamWriter == nullptr)
+ {
+ // TODO: Add error handling.
+ return;
+ }
+
+ EventPipeFile *pFile = new (nothrow) EventPipeFile(pStreamWriter);
if (pFile == NULL)
{
// TODO: Add error handling.
@@ -576,14 +664,13 @@ void EventPipe::SwitchToNextFile()
s_pFile = pFile;
}
-void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath)
+void EventPipe::GetNextFilePath(SString &nextTraceFilePath)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
- PRECONDITION(pSession != NULL);
PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
@@ -592,8 +679,7 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi
nextTraceFilePath.Set(s_pOutputPath);
// If multiple files have been requested, then add a sequence number to the trace file name.
- UINT64 multiFileTraceLengthInSeconds = pSession->GetMultiFileTraceLengthInSeconds();
- if (multiFileTraceLengthInSeconds > 0)
+ if (s_multiFileTraceLengthInSeconds > 0)
{
// Remove the ".netperf" file extension if it exists.
SString::Iterator netPerfExtension = nextTraceFilePath.End();
@@ -915,20 +1001,6 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
return SWA_CONTINUE;
}
-EventPipeConfiguration *EventPipe::GetConfiguration()
-{
- LIMITED_METHOD_CONTRACT;
-
- return s_pConfig;
-}
-
-CrstStatic *EventPipe::GetLock()
-{
- LIMITED_METHOD_CONTRACT;
-
- return &s_configCrst;
-}
-
void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv)
{
CONTRACTL
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index 494a8c5c98..5129baf9a9 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -20,6 +20,7 @@ class EventPipeProvider;
class MethodDesc;
struct EventPipeProviderConfiguration;
class EventPipeSession;
+class IpcStream;
// EVENT_FILTER_DESCRIPTOR (This type does not exist on non-Windows platforms.)
// https://docs.microsoft.com/en-us/windows/desktop/api/evntprov/ns-evntprov-_event_filter_descriptor
@@ -73,7 +74,15 @@ private:
public:
// Build this payload with a flat buffer inside
- EventPipeEventPayload(BYTE *pData, unsigned int length);
+ EventPipeEventPayload(BYTE *pData, unsigned int length) :
+ m_pData(pData),
+ m_pEventData(nullptr),
+ m_eventDataCount(0),
+ m_size(length),
+ m_allocatedData(false)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
// Build this payload to contain an array of EventData objects
EventPipeEventPayload(EventData *pEventData, unsigned int eventDataCount);
@@ -94,7 +103,6 @@ public:
bool IsFlattened() const
{
LIMITED_METHOD_CONTRACT;
-
return m_pData != NULL;
}
@@ -102,14 +110,12 @@ public:
unsigned int GetSize() const
{
LIMITED_METHOD_CONTRACT;
-
return m_size;
}
EventData *GetEventDataArray() const
{
LIMITED_METHOD_CONTRACT;
-
return m_pEventData;
}
};
@@ -136,7 +142,6 @@ public:
StackContents()
{
LIMITED_METHOD_CONTRACT;
-
Reset();
}
@@ -155,21 +160,18 @@ public:
void Reset()
{
LIMITED_METHOD_CONTRACT;
-
m_nextAvailableFrame = 0;
}
bool IsEmpty()
{
LIMITED_METHOD_CONTRACT;
-
return (m_nextAvailableFrame == 0);
}
unsigned int GetLength()
{
LIMITED_METHOD_CONTRACT;
-
return m_nextAvailableFrame;
}
@@ -218,19 +220,18 @@ public:
BYTE *GetPointer() const
{
LIMITED_METHOD_CONTRACT;
-
return (BYTE *)m_stackFrames;
}
unsigned int GetSize() const
{
LIMITED_METHOD_CONTRACT;
-
return (m_nextAvailableFrame * sizeof(UINT_PTR));
}
};
typedef UINT64 EventPipeSessionID;
+typedef void (*FlushTimerCallback)();
class EventPipe
{
@@ -257,6 +258,13 @@ public:
uint32_t numProviders,
uint64_t multiFileTraceLengthInSeconds);
+ static EventPipeSessionID Enable(
+ IpcStream *pStream,
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders);
+
// Disable tracing via the event pipe.
static void Disable(EventPipeSessionID id);
@@ -303,33 +311,47 @@ private:
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 EventPipeSessionID Enable(
+ EventPipeSession *const pSession,
+ WAITORTIMERCALLBACK callback,
+ DWORD dueTime,
+ DWORD period);
- static void CreateFileSwitchTimer();
+ static void CreateFlushTimerCallback(WAITORTIMERCALLBACK Callback, DWORD DueTime, DWORD Period);
- static void DeleteFileSwitchTimer();
+ static void DeleteFlushTimerCallback();
// 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);
+ static void WINAPI FlushTimer(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);
+ static void GetNextFilePath(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();
+ static EventPipeConfiguration *GetConfiguration()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return s_pConfig;
+ }
// Get the event pipe configuration lock.
- static CrstStatic *GetLock();
+ static CrstStatic *GetLock()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &s_configCrst;
+ }
static CrstStatic s_configCrst;
static bool s_tracingInitialized;
@@ -341,9 +363,9 @@ private:
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;
+ static ULONGLONG s_lastFlushSwitchTime;
+ static uint64_t s_multiFileTraceLengthInSeconds;
};
struct EventPipeProviderConfiguration
diff --git a/src/vm/eventpipeblock.cpp b/src/vm/eventpipeblock.cpp
index 5992b874de..d76cd3a71d 100644
--- a/src/vm/eventpipeblock.cpp
+++ b/src/vm/eventpipeblock.cpp
@@ -10,7 +10,8 @@
#ifdef FEATURE_PERFTRACING
-EventPipeBlock::EventPipeBlock(unsigned int maxBlockSize)
+EventPipeBlock::EventPipeBlock(unsigned int maxBlockSize) :
+ FastSerializableObject(1, 0)
{
CONTRACTL
{
@@ -71,7 +72,7 @@ bool EventPipeBlock::WriteEvent(EventPipeEventInstance &instance)
return false;
}
- BYTE* alignedEnd = m_pWritePointer + totalSize + sizeof(totalSize);
+ BYTE* alignedEnd = m_pWritePointer + totalSize + sizeof(totalSize);
memcpy(m_pWritePointer, &totalSize, sizeof(totalSize));
m_pWritePointer += sizeof(totalSize);
diff --git a/src/vm/eventpipeblock.h b/src/vm/eventpipeblock.h
index 30bd458f38..78b04af805 100644
--- a/src/vm/eventpipeblock.h
+++ b/src/vm/eventpipeblock.h
@@ -11,80 +11,69 @@
#include "fastserializableobject.h"
#include "fastserializer.h"
-class EventPipeBlock : public FastSerializableObject
+class EventPipeBlock final : public FastSerializableObject
{
- public:
- EventPipeBlock(unsigned int maxBlockSize);
+public:
+ EventPipeBlock(unsigned int maxBlockSize);
+ ~EventPipeBlock();
+
+ // Write an event to the block.
+ // Returns:
+ // - true: The write succeeded.
+ // - false: The write failed. In this case, the block should be considered full.
+ bool WriteEvent(EventPipeEventInstance &instance);
+
+ void Clear();
+
+ const char *GetTypeName() override
+ {
+ LIMITED_METHOD_CONTRACT;
+ return "EventBlock";
+ }
+
+ void FastSerialize(FastSerializer *pSerializer) override
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pSerializer != NULL);
+ }
+ CONTRACTL_END;
- ~EventPipeBlock();
+ if (m_pBlock == NULL)
+ return;
- // Write an event to the block.
- // Returns:
- // - true: The write succeeded.
- // - false: The write failed. In this case, the block should be considered full.
- bool WriteEvent(EventPipeEventInstance &instance);
+ unsigned int eventsSize = (unsigned int)(m_pWritePointer - m_pBlock);
+ pSerializer->WriteBuffer((BYTE *)&eventsSize, sizeof(eventsSize));
- void Clear();
+ if (eventsSize == 0)
+ return;
- const char* GetTypeName()
+ size_t currentPosition = pSerializer->GetCurrentPosition();
+ if (currentPosition % ALIGNMENT_SIZE != 0)
{
- LIMITED_METHOD_CONTRACT;
- return "EventBlock";
- }
+ BYTE maxPadding[ALIGNMENT_SIZE - 1] = {}; // it's longest possible padding, we are going to use only part of it
+ unsigned int paddingLength = ALIGNMENT_SIZE - (currentPosition % ALIGNMENT_SIZE);
+ pSerializer->WriteBuffer(maxPadding, paddingLength); // we write zeros here, the reader is going to always read from the first aligned address of the serialized content
- void FastSerialize(FastSerializer *pSerializer)
- {
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- PRECONDITION(pSerializer != NULL);
- }
- CONTRACTL_END;
-
- if (m_pBlock == NULL)
- {
- return;
- }
-
- unsigned int eventsSize = (unsigned int)(m_pWritePointer - m_pBlock);
- pSerializer->WriteBuffer((BYTE*)&eventsSize, sizeof(eventsSize));
-
- if (eventsSize == 0)
- {
- return;
- }
-
- size_t currentPosition = pSerializer->GetCurrentPosition();
- if (currentPosition % ALIGNMENT_SIZE != 0)
- {
- BYTE maxPadding[ALIGNMENT_SIZE - 1] = {}; // it's longest possible padding, we are going to use only part of it
- unsigned int paddingLength = ALIGNMENT_SIZE - (currentPosition % ALIGNMENT_SIZE);
- pSerializer->WriteBuffer(maxPadding, paddingLength); // we write zeros here, the reader is going to always read from the first aligned address of the serialized content
-
- _ASSERTE(pSerializer->GetCurrentPosition() % ALIGNMENT_SIZE == 0);
- }
-
- pSerializer->WriteBuffer(m_pBlock, eventsSize);
+ _ASSERTE(pSerializer->HasWriteErrors() || (pSerializer->GetCurrentPosition() % ALIGNMENT_SIZE == 0));
}
- private:
- BYTE *m_pBlock;
- BYTE *m_pWritePointer;
- BYTE *m_pEndOfTheBuffer;
+ pSerializer->WriteBuffer(m_pBlock, eventsSize);
+ }
- unsigned int GetSize() const
- {
- LIMITED_METHOD_CONTRACT;
-
- if (m_pBlock == NULL)
- {
- return 0;
- }
+private:
+ BYTE *m_pBlock;
+ BYTE *m_pWritePointer;
+ BYTE *m_pEndOfTheBuffer;
- return (unsigned int)(m_pEndOfTheBuffer - m_pBlock);
- }
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pBlock == nullptr ? 0 : (unsigned int)(m_pEndOfTheBuffer - m_pBlock);
+ }
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp
index 4194abc73c..e15676e15e 100644
--- a/src/vm/eventpipebuffermanager.cpp
+++ b/src/vm/eventpipebuffermanager.cpp
@@ -263,7 +263,7 @@ EventPipeBufferList* EventPipeBufferManager::FindThreadToStealFrom()
pOldestContainingList = pCandidate;
}
// Otherwise, to replace the existing candidate, this candidate must have an older timestamp in its oldest buffer.
- else if((pOldestContainingList->GetHead()->GetMostRecentTimeStamp().QuadPart) >
+ else if((pOldestContainingList->GetHead()->GetMostRecentTimeStamp().QuadPart) >
(pCandidate->GetHead()->GetMostRecentTimeStamp().QuadPart))
{
pOldestContainingList = pCandidate;
@@ -414,7 +414,7 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
THROWS;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION(pFile != NULL);
+ PRECONDITION(pFile != nullptr);
}
CONTRACTL_END;
@@ -451,7 +451,7 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
{
// If it's the oldest event we've seen, then save it.
if((pOldestInstance == NULL) ||
- (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))
+ (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))
{
pOldestInstance = pNext;
pOldestContainingBuffer = pContainingBuffer;
diff --git a/src/vm/eventpipebuffermanager.h b/src/vm/eventpipebuffermanager.h
index e10018fe8e..a7584cce1d 100644
--- a/src/vm/eventpipebuffermanager.h
+++ b/src/vm/eventpipebuffermanager.h
@@ -18,7 +18,7 @@ class EventPipeBufferList;
// This class is a TLS wrapper around a pointer to thread-specific EventPipeBufferList
// The struct wrapper is present mainly because we need a way to free the EventPipeBufferList
// when the thread that owns it dies. Placing this class as a TLS variable will call ~ThreadEventBufferList()
-// when the thread dies so we can free EventPipeBufferList in the destructor.
+// when the thread dies so we can free EventPipeBufferList in the destructor.
class ThreadEventBufferList
{
#ifndef __GNUC__
@@ -103,7 +103,7 @@ public:
// Write the contents of the managed buffers to the specified file.
// The stopTimeStamp is used to determine when tracing was stopped to ensure that we
// skip any events that might be partially written due to races when tracing is stopped.
- void WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp);
+ void WriteAllBuffersToFile(EventPipeFile *pFastSerializableObject, LARGE_INTEGER stopTimeStamp);
// Attempt to de-allocate resources as best we can. It is possible for some buffers to leak because
// threads can be in the middle of a write operation and get blocked, and we may not get an opportunity
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
index 4667276753..48899df16c 100644
--- a/src/vm/eventpipeconfiguration.cpp
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -312,8 +312,7 @@ EventPipeSession *EventPipeConfiguration::CreateSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders,
- uint64_t multiFileTraceLengthInSeconds)
+ uint32_t numProviders)
{
CONTRACTL
{
@@ -328,8 +327,7 @@ EventPipeSession *EventPipeConfiguration::CreateSession(
sessionType,
circularBufferSizeInMB,
pProviders,
- numProviders,
- multiFileTraceLengthInSeconds);
+ numProviders);
}
void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
index 6021305b82..270ed449fc 100644
--- a/src/vm/eventpipeconfiguration.h
+++ b/src/vm/eventpipeconfiguration.h
@@ -55,8 +55,7 @@ public:
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders,
- uint64_t multiFileTraceLengthInSeconds = 0);
+ uint32_t numProviders);
// Delete a session.
void DeleteSession(EventPipeSession *pSession);
diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp
index 2e98fd5ad7..73fb48e2e3 100644
--- a/src/vm/eventpipefile.cpp
+++ b/src/vm/eventpipefile.cpp
@@ -3,15 +3,13 @@
// See the LICENSE file in the project root for more information.
#include "common.h"
-#include "eventpipebuffer.h"
#include "eventpipeblock.h"
-#include "eventpipeconfiguration.h"
#include "eventpipefile.h"
#include "sampleprofiler.h"
#ifdef FEATURE_PERFTRACING
-EventPipeFile::EventPipeFile(SString &outputFilePath)
+EventPipeFile::EventPipeFile(StreamWriter *pStreamWriter) : FastSerializableObject(3, 0)
{
CONTRACTL
{
@@ -21,9 +19,6 @@ EventPipeFile::EventPipeFile(SString &outputFilePath)
}
CONTRACTL_END;
- SetObjectVersion(3);
- SetMinReaderVersion(0);
-
m_pBlock = new EventPipeBlock(100 * 1024);
// File start time information.
@@ -42,7 +37,7 @@ EventPipeFile::EventPipeFile(SString &outputFilePath)
m_samplingRateInNs = SampleProfiler::GetSamplingRate();
// Create the file stream and write the header.
- m_pSerializer = new FastSerializer(new FileStreamWriter(outputFilePath));
+ m_pSerializer = new FastSerializer(pStreamWriter);
m_serializationLock.Init(LOCK_TYPE_DEFAULT);
m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>();
@@ -65,21 +60,10 @@ EventPipeFile::~EventPipeFile()
CONTRACTL_END;
if (m_pBlock != NULL && m_pSerializer != NULL)
- {
WriteEnd();
- }
-
- if (m_pBlock != NULL)
- {
- delete(m_pBlock);
- m_pBlock = NULL;
- }
- if(m_pSerializer != NULL)
- {
- delete(m_pSerializer);
- m_pSerializer = NULL;
- }
+ delete m_pBlock;
+ delete m_pSerializer;
}
void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
@@ -105,8 +89,8 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
SaveMetadataId(*instance.GetEvent(), metadataId);
- delete[] (pMetadataInstance->GetData());
- delete (pMetadataInstance);
+ delete[] pMetadataInstance->GetData();
+ delete pMetadataInstance;
}
WriteToBlock(instance, metadataId);
@@ -144,9 +128,7 @@ void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int
instance.SetMetadataId(metadataId);
if (m_pBlock->WriteEvent(instance))
- {
return; // the block is not full, we added the event and continue
- }
// we can't write this event to the current block (it's full)
// so we write what we have in the block to the serializer
@@ -209,9 +191,7 @@ void EventPipeFile::SaveMetadataId(EventPipeEvent &event, unsigned int metadataI
// If a pre-existing metadata label exists, remove it.
unsigned int oldId;
if(m_pMetadataIds->Lookup(&event, &oldId))
- {
m_pMetadataIds->Remove(&event);
- }
// Add the metadata label.
m_pMetadataIds->Add(&event, metadataId);
diff --git a/src/vm/eventpipefile.h b/src/vm/eventpipefile.h
index 29c2b19457..d99bcd1883 100644
--- a/src/vm/eventpipefile.h
+++ b/src/vm/eventpipefile.h
@@ -2,7 +2,6 @@
// 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_FILE_H__
#define __EVENTPIPE_FILE_H__
@@ -12,87 +11,86 @@
#include "eventpipeblock.h"
#include "eventpipeeventinstance.h"
#include "fastserializableobject.h"
-#include "fastserializer.h"
-class EventPipeFile : public FastSerializableObject
-{
- public:
+class FastSerializer;
- EventPipeFile(SString &outputFilePath);
- ~EventPipeFile();
+class EventPipeFile final : public FastSerializableObject
+{
+public:
+ EventPipeFile(StreamWriter *pStreamWriter);
+ ~EventPipeFile();
- void WriteEvent(EventPipeEventInstance &instance);
+ void WriteEvent(EventPipeEventInstance &instance);
- void WriteEnd();
+ const char *GetTypeName() override
+ {
+ LIMITED_METHOD_CONTRACT;
+ return "Trace";
+ }
- const char* GetTypeName()
+ void FastSerialize(FastSerializer *pSerializer) override
+ {
+ CONTRACTL
{
- LIMITED_METHOD_CONTRACT;
- return "Trace";
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pSerializer != NULL);
}
+ CONTRACTL_END;
- void FastSerialize(FastSerializer *pSerializer)
- {
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- PRECONDITION(pSerializer != NULL);
- }
- CONTRACTL_END;
-
- pSerializer->WriteBuffer((BYTE*)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime));
- pSerializer->WriteBuffer((BYTE*)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp));
- pSerializer->WriteBuffer((BYTE*)&m_timeStampFrequency, sizeof(m_timeStampFrequency));
-
- // the beginning of V3
- pSerializer->WriteBuffer((BYTE*)&m_pointerSize, sizeof(m_pointerSize));
- pSerializer->WriteBuffer((BYTE*)&m_currentProcessId, sizeof(m_currentProcessId));
- pSerializer->WriteBuffer((BYTE*)&m_numberOfProcessors, sizeof(m_numberOfProcessors));
- pSerializer->WriteBuffer((BYTE*)&m_samplingRateInNs, sizeof(m_samplingRateInNs));
- }
+ pSerializer->WriteBuffer((BYTE *)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime));
+ pSerializer->WriteBuffer((BYTE *)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp));
+ pSerializer->WriteBuffer((BYTE *)&m_timeStampFrequency, sizeof(m_timeStampFrequency));
+
+ // the beginning of V3
+ pSerializer->WriteBuffer((BYTE *)&m_pointerSize, sizeof(m_pointerSize));
+ pSerializer->WriteBuffer((BYTE *)&m_currentProcessId, sizeof(m_currentProcessId));
+ pSerializer->WriteBuffer((BYTE *)&m_numberOfProcessors, sizeof(m_numberOfProcessors));
+ pSerializer->WriteBuffer((BYTE *)&m_samplingRateInNs, sizeof(m_samplingRateInNs));
+ }
- private:
+private:
+ void WriteEnd();
- unsigned int GenerateMetadataId();
+ unsigned int GenerateMetadataId();
- unsigned int GetMetadataId(EventPipeEvent &event);
+ unsigned int GetMetadataId(EventPipeEvent &event);
- void SaveMetadataId(EventPipeEvent &event, unsigned int metadataId);
+ void SaveMetadataId(EventPipeEvent &event, unsigned int metadataId);
- void WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId);
+ void WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId);
- // The object responsible for serialization.
- FastSerializer *m_pSerializer;
+ // The object responsible for serialization.
+ FastSerializer *m_pSerializer;
- EventPipeBlock *m_pBlock;
+ EventPipeBlock *m_pBlock;
- // The system time when the file was opened.
- SYSTEMTIME m_fileOpenSystemTime;
+ // The system time when the file was opened.
+ SYSTEMTIME m_fileOpenSystemTime;
- // The timestamp when the file was opened. Used for calculating file-relative timestamps.
- LARGE_INTEGER m_fileOpenTimeStamp;
+ // The timestamp when the file was opened. Used for calculating file-relative timestamps.
+ LARGE_INTEGER m_fileOpenTimeStamp;
- // The frequency of the timestamps used for this file.
- LARGE_INTEGER m_timeStampFrequency;
+ // The frequency of the timestamps used for this file.
+ LARGE_INTEGER m_timeStampFrequency;
- unsigned int m_pointerSize;
+ unsigned int m_pointerSize;
- unsigned int m_currentProcessId;
+ unsigned int m_currentProcessId;
- unsigned int m_numberOfProcessors;
+ unsigned int m_numberOfProcessors;
- unsigned int m_samplingRateInNs;
+ unsigned int m_samplingRateInNs;
- // The serialization which is responsible for making sure only a single event
- // or block of events gets written to the file at once.
- SpinLock m_serializationLock;
+ // The serialization which is responsible for making sure only a single event
+ // or block of events gets written to the file at once.
+ SpinLock m_serializationLock;
- // Hashtable of metadata labels.
- MapSHashWithRemove<EventPipeEvent*, unsigned int> *m_pMetadataIds;
+ // Hashtable of metadata labels.
+ MapSHashWithRemove<EventPipeEvent *, unsigned int> *m_pMetadataIds;
- Volatile<LONG> m_metadataIdCounter;
+ Volatile<LONG> m_metadataIdCounter;
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeprotocolhelper.cpp b/src/vm/eventpipeprotocolhelper.cpp
index bfcdcfa2f8..c1fe613c05 100644
--- a/src/vm/eventpipeprotocolhelper.cpp
+++ b/src/vm/eventpipeprotocolhelper.cpp
@@ -9,7 +9,7 @@
#ifdef FEATURE_PERFTRACING
-bool EventPipeProtocolHelper::TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
+bool EventPipeProtocolHelper::TryParseProviderConfiguration(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
{
// Picking an arbitrary upper bound,
// This should be larger than any reasonable client request.
@@ -62,8 +62,7 @@ void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
CONTRACTL_END;
// TODO: Read within a loop.
- const uint32_t BufferSize = 8192;
- uint8_t buffer[BufferSize]{};
+ uint8_t buffer[IpcStreamReadBufferSize]{};
uint32_t nNumberOfBytesRead = 0;
bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead);
if (!fSuccess)
@@ -93,9 +92,11 @@ void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
!TryParse(pBufferCursor, bufferLen, multiFileTraceLengthInSeconds) ||
!TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
- !TryParseProviderConfigurations(pBufferCursor, bufferLen, providerConfigs))
+ !TryParseProviderConfiguration(pBufferCursor, bufferLen, providerConfigs))
{
- return; // TODO: error handling
+ // TODO: error handling
+ delete pStream;
+ return;
}
EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
@@ -127,7 +128,7 @@ void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
delete pStream;
}
-void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
+void EventPipeProtocolHelper::DisableFileTracingEventHandler(IpcStream *pStream)
{
CONTRACTL
{
@@ -140,7 +141,7 @@ void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
uint32_t nNumberOfBytesRead = 0;
EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
- const bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead);
+ bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead);
if (!fSuccess || nNumberOfBytesRead != sizeof(sessionId))
{
// TODO: Add error handling.
@@ -149,8 +150,80 @@ void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
}
EventPipe::Disable(sessionId);
- // TODO: Should we acknowledge back?
+ 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::AttachTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ // TODO: Read within a loop.
+ uint8_t buffer[IpcStreamReadBufferSize]{};
+ 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
+ // 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) ||
+ !TryParseProviderConfiguration(pBufferCursor, bufferLen, providerConfigs))
+ {
+ // TODO: error handling
+ delete pStream;
+ return;
+ }
+
+ if (providerConfigs.Size() > 0)
+ {
+ EventPipe::Enable(
+ pStream, // IPC stream
+ circularBufferSizeInMB, // circularBufferSizeInMB
+ DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
+ providerConfigs.Ptr(), // pConfigs
+ static_cast<uint32_t>(providerConfigs.Size())); // numConfigs
+ }
+}
+
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeprotocolhelper.h b/src/vm/eventpipeprotocolhelper.h
index fb3f65a247..44192d724a 100644
--- a/src/vm/eventpipeprotocolhelper.h
+++ b/src/vm/eventpipeprotocolhelper.h
@@ -17,17 +17,17 @@ class EventPipeProtocolHelper
public:
// IPC event handlers.
static void EnableFileTracingEventHandler(IpcStream *pStream);
- static void DisableTracingEventHandler(IpcStream *pStream);
+ static void DisableFileTracingEventHandler(IpcStream *pStream);
+
+ static void AttachTracingEventHandler(IpcStream *pStream);
private:
const static uint32_t DefaultCircularBufferMB = 1024; // 1 GB
const static uint64_t DefaultMultiFileTraceLengthInSeconds = 0;
- const static uint32_t DefaultProfilerSamplingRateInNanoseconds = 1000000;
+ const static uint32_t DefaultProfilerSamplingRateInNanoseconds = 1000000; // 1 msec.
+ const static uint32_t IpcStreamReadBufferSize = 8192;
- //! 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);
+ static bool TryParseProviderConfiguration(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result);
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp
index 03bc6c681a..30c88fb807 100644
--- a/src/vm/eventpipesession.cpp
+++ b/src/vm/eventpipesession.cpp
@@ -14,8 +14,7 @@ EventPipeSession::EventPipeSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders,
- uint64_t multiFileTraceLengthInSeconds)
+ uint32_t numProviders)
{
CONTRACTL
{
@@ -30,7 +29,6 @@ EventPipeSession::EventPipeSession(
m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB;
m_rundownEnabled = false;
m_pProviderList = new EventPipeSessionProviderList(pProviders, numProviders);
- m_multiFileTraceLengthInSeconds = multiFileTraceLengthInSeconds;
GetSystemTimeAsFileTime(&m_sessionStartTime);
QueryPerformanceCounter(&m_sessionStartTimeStamp);
}
diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h
index 0474c7cafe..128ba77012 100644
--- a/src/vm/eventpipesession.h
+++ b/src/vm/eventpipesession.h
@@ -39,9 +39,6 @@ private:
// Start timestamp.
LARGE_INTEGER m_sessionStartTimeStamp;
- // The maximum trace length in seconds. Used to determine when to flush the current file and start a new one.
- UINT64 m_multiFileTraceLengthInSeconds;
-
public:
// TODO: This needs to be exposed via EventPipe::CreateSession() and EventPipe::DeleteSession() to avoid memory ownership issues.
@@ -49,8 +46,7 @@ public:
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders,
- uint64_t multiFileTraceLengthInSeconds);
+ uint32_t numProviders);
~EventPipeSession();
// Determine if the session is valid or not. Invalid sessions can be detected before they are enabled.
@@ -98,12 +94,6 @@ public:
return m_sessionStartTimeStamp;
}
- UINT64 GetMultiFileTraceLengthInSeconds() const
- {
- LIMITED_METHOD_CONTRACT;
- return m_multiFileTraceLengthInSeconds;
- }
-
// Add a new provider to the session.
void AddSessionProvider(EventPipeSessionProvider *pProvider);
diff --git a/src/vm/fastserializableobject.h b/src/vm/fastserializableobject.h
index ec162e3cc3..e411dff74c 100644
--- a/src/vm/fastserializableobject.h
+++ b/src/vm/fastserializableobject.h
@@ -11,10 +11,13 @@ class FastSerializer;
class FastSerializableObject
{
-
public:
+ FastSerializableObject(int objectVersion, int minReaderVersion) :
+ m_objectVersion(objectVersion), m_minReaderVersion(minReaderVersion)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
- // Virtual destructor to ensure that derived class destructors get called.
virtual ~FastSerializableObject()
{
LIMITED_METHOD_CONTRACT;
@@ -24,42 +27,23 @@ public:
virtual void FastSerialize(FastSerializer *pSerializer) = 0;
// Get the type name for the current object.
- virtual const char* GetTypeName() = 0;
+ virtual const char *GetTypeName() = 0;
int GetObjectVersion() const
{
LIMITED_METHOD_CONTRACT;
-
return m_objectVersion;
}
int GetMinReaderVersion() const
{
LIMITED_METHOD_CONTRACT;
-
return m_minReaderVersion;
}
-protected:
-
- void SetObjectVersion(int version)
- {
- LIMITED_METHOD_CONTRACT;
-
- m_objectVersion = version;
- }
-
- void SetMinReaderVersion(int version)
- {
- LIMITED_METHOD_CONTRACT;
-
- m_minReaderVersion = version;
- }
-
private:
-
- int m_objectVersion = 1;
- int m_minReaderVersion = 0;
+ const int m_objectVersion;
+ const int m_minReaderVersion;
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp
index e02ec93010..2941f6ac6a 100644
--- a/src/vm/fastserializer.cpp
+++ b/src/vm/fastserializer.cpp
@@ -49,6 +49,8 @@ bool IpcStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite,
}
CONTRACTL_END;
+ if (_pStream == nullptr)
+ return false;
return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten);
}
@@ -97,6 +99,9 @@ bool FileStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite,
}
CONTRACTL_END;
+ if (m_pFileStream == nullptr)
+ return false;
+
ULONG outCount;
HRESULT hResult = m_pFileStream->Write(lpBuffer, nBytesToWrite, &outCount);
nBytesWritten = static_cast<uint32_t>(outCount);
@@ -177,16 +182,15 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
size_t prevPos = m_currentPos;
#endif
m_currentPos += outCount;
+
+ // This will cause us to stop writing to the file.
+ // The file will still remain open until shutdown so that we don't
+ // have to take a lock at this level when we touch the file stream.
+ m_writeErrorEncountered = (length != outCount);
+
#ifdef _DEBUG
- _ASSERTE(prevPos < m_currentPos);
+ _ASSERTE(m_writeErrorEncountered || (prevPos < m_currentPos));
#endif
-
- if (length != outCount)
- {
- // This will cause us to stop writing to the file.
- // The file will still remain open until shutdown so that we don't have to take a lock at this level when we touch the file stream.
- m_writeErrorEncountered = true;
- }
}
EX_CATCH
{
diff --git a/src/vm/fastserializer.h b/src/vm/fastserializer.h
index 98208c0eb1..4d61adceeb 100644
--- a/src/vm/fastserializer.h
+++ b/src/vm/fastserializer.h
@@ -92,6 +92,12 @@ public:
return m_currentPos;
}
+ bool HasWriteErrors() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_writeErrorEncountered;
+ }
+
private:
void WriteSerializationType(FastSerializableObject *pObject);
void WriteFileHeader();