<MicrosoftNETCoreRuntimeCoreCLRPackageVersion>3.0.0-preview5-27615-71</MicrosoftNETCoreRuntimeCoreCLRPackageVersion>
<XunitPackageVersion>2.4.1-pre.build.4059</XunitPackageVersion>
<XunitPerformanceApiPackageVersion>1.0.0-beta-build0015</XunitPerformanceApiPackageVersion>
- <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.36</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
+ <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.40</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
<IbcMergePackageVersion>4.6.0-alpha-00001</IbcMergePackageVersion>
<CommandLineParserVersion>2.2.0</CommandLineParserVersion>
return new IpcStream(hPipe);
}
-void IpcStream::DiagnosticsIpc::Unlink(ErrorCallback callback)
+void IpcStream::DiagnosticsIpc::Unlink(ErrorCallback)
{
}
switch (header.RequestType)
{
- case DiagnosticMessageType::EnableEventPipe:
- EventPipeProtocolHelper::EnableFileTracingEventHandler(pStream);
+ case DiagnosticMessageType::StopEventPipeTracing:
+ EventPipeProtocolHelper::StopTracing(pStream);
break;
- case DiagnosticMessageType::DisableEventPipe:
- EventPipeProtocolHelper::DisableFileTracingEventHandler(pStream);
- break;
-
- case DiagnosticMessageType::StreamEventPipe:
- EventPipeProtocolHelper::AttachTracingEventHandler(pStream);
+ case DiagnosticMessageType::CollectEventPipeTracing:
+ EventPipeProtocolHelper::CollectTracing(pStream);
break;
default:
- LOG((LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType));
+ STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", header.RequestType);
+ delete pStream;
break;
}
}
///////////////////////////////////////////////////////////////////////////
// EventPipe
- EnableEventPipe = 1024,
- DisableEventPipe,
- StreamEventPipe,
- AttachEventPipe,
- DetachEventPipe,
+ StartEventPipeTracing = 1024, // To file
+ StopEventPipeTracing,
+ CollectEventPipeTracing, // To IPC
///////////////////////////////////////////////////////////////////////////
// Profiler = 2048
uint32_t circularBufferSizeInMB,
uint64_t profilerSamplingRateInNanoseconds,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders)
+ uint32_t numProviders,
+ EventPipeSessionType sessionType,
+ IpcStream *const pStream)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
- PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
+ PRECONDITION(circularBufferSizeInMB > 0);
+ PRECONDITION(profilerSamplingRateInNanoseconds > 0);
+ PRECONDITION(numProviders > 0 && pProviders != nullptr);
}
CONTRACTL_END;
CrstHolder _crst(GetLock());
// Create a new session.
- SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
+ SampleProfiler::SetSamplingRate(static_cast<unsigned long>(profilerSamplingRateInNanoseconds));
EventPipeSession *pSession = s_pConfig->CreateSession(
- (strOutputPath != nullptr) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
+ sessionType,
circularBufferSizeInMB,
pProviders,
numProviders);
- // 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)
- s_pFile = new EventPipeFile(new FileStreamWriter(SString(strOutputPath)));
- return Enable(pSession);
-}
-
-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);
-
- // Initialize the last file switch time.
- s_lastFlushSwitchTime = CLRGetTickCount64();
-
- // 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.
- const DWORD FlushTimerPeriodMS = 100; // TODO: Define a good number here for streaming.
- return Enable(pSession, FlushTimer, FlushTimerPeriodMS, FlushTimerPeriodMS);
+ EventPipeSessionID sessionId = Enable(strOutputPath, pSession, sessionType, pStream);
+ return sessionId;
}
EventPipeSessionID EventPipe::Enable(
+ LPCWSTR strOutputPath,
EventPipeSession *const pSession,
- WAITORTIMERCALLBACK callback,
- DWORD dueTime,
- DWORD period)
+ EventPipeSessionType sessionType,
+ IpcStream *const pStream)
{
CONTRACTL
{
}
CONTRACTL_END;
- // If tracing is not initialized or is already enabled, bail here.
- if (!s_tracingInitialized || s_pConfig == nullptr || s_pConfig->Enabled())
+ // If the state or arguments are invalid, bail here.
+ if (pSession == nullptr || !pSession->IsValid())
+ return 0;
+ if (sessionType == EventPipeSessionType::File && strOutputPath == nullptr)
+ return 0;
+ if (sessionType == EventPipeSessionType::IpcStream && pStream == nullptr)
return 0;
- // If the state or arguments are invalid, bail here.
- if (pSession == NULL || !pSession->IsValid())
+ // If tracing is not initialized or is already enabled, bail here.
+ if (!s_tracingInitialized || s_pConfig == nullptr || s_pConfig->Enabled())
return 0;
// Enable the EventPipe EventSource.
// Save the session.
s_pSession = pSession;
+ EventPipeSessionID sessionId = reinterpret_cast<EventPipeSessionID>(s_pSession);
+
+ // 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.
+ switch (sessionType)
+ {
+ case EventPipeSessionType::File:
+ if (strOutputPath != nullptr)
+ s_pFile = new EventPipeFile(new FileStreamWriter(SString(strOutputPath)));
+ break;
+
+ case EventPipeSessionType::IpcStream:
+ s_pFile = new EventPipeFile(new IpcStreamWriter(sessionId, pStream));
+ CreateFlushTimerCallback();
+ break;
+
+ default:
+ s_pFile = nullptr;
+ break;
+ }
// Enable tracing.
s_pConfig->Enable(s_pSession);
// Enable the sample profiler
SampleProfiler::Enable();
- if (callback != nullptr)
- CreateFlushTimerCallback(callback, dueTime, period);
-
// Return the session ID.
- return (EventPipeSessionID)s_pSession;
+ return sessionId;
}
void EventPipe::Disable(EventPipeSessionID id)
}
}
-void EventPipe::CreateFlushTimerCallback(WAITORTIMERCALLBACK callback, DWORD dueTime, DWORD period)
+void EventPipe::CreateFlushTimerCallback()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
- PRECONDITION(callback != nullptr);
- PRECONDITION(dueTime > 0);
- PRECONDITION(period > 0);
PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END
timerContextHolder->TimerId = 0;
+ // Initialize the last file switch time.
+ s_lastFlushSwitchTime = CLRGetTickCount64();
+
bool success = false;
_ASSERTE(s_fileSwitchTimerHandle == NULL);
EX_TRY
{
if (ThreadpoolMgr::CreateTimerQueueTimer(
&s_fileSwitchTimerHandle,
- callback,
+ FlushTimer,
timerContextHolder,
- dueTime,
- period,
+ 100, // DueTime (msec)
+ 100, // Period (msec)
0 /* flags */))
{
_ASSERTE(s_fileSwitchTimerHandle != NULL);
struct EventPipeProviderConfiguration;
class EventPipeSession;
class IpcStream;
+enum class EventPipeSessionType;
// 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
// determines which events are reported and traced. The structure gives the
// event provider greater control over the selection of events for reporting
// and tracing.
+// TODO: EventFilterDescriptor and EventData (defined below) are the same.
struct EventFilterDescriptor
{
// A pointer to the filter data.
};
typedef UINT64 EventPipeSessionID;
-typedef void (*FlushTimerCallback)();
class EventPipe
{
uint32_t circularBufferSizeInMB,
uint64_t profilerSamplingRateInNanoseconds,
const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders);
-
- static EventPipeSessionID Enable(
- IpcStream *pStream,
- uint32_t circularBufferSizeInMB,
- uint64_t profilerSamplingRateInNanoseconds,
- const EventPipeProviderConfiguration *pProviders,
- uint32_t numProviders);
+ uint32_t numProviders,
+ EventPipeSessionType sessionType,
+ IpcStream *const pStream);
// Disable tracing via the event pipe.
static void Disable(EventPipeSessionID id);
// Enable the specified EventPipe session.
static EventPipeSessionID Enable(
+ LPCWSTR strOutputPath,
EventPipeSession *const pSession,
- WAITORTIMERCALLBACK callback = nullptr,
- DWORD dueTime = 0,
- DWORD period = 0);
+ EventPipeSessionType sessionType,
+ IpcStream *const pStream);
- static void CreateFlushTimerCallback(WAITORTIMERCALLBACK Callback, DWORD DueTime, DWORD Period);
+ static void CreateFlushTimerCallback();
static void DeleteFlushTimerCallback();
THROWS;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
+ PRECONDITION(circularBufferSizeInMB > 0);
+ PRECONDITION(numProviders > 0 && pProviders != nullptr);
}
CONTRACTL_END;
UINT64 QCALLTYPE EventPipeInternal::Enable(
__in_z LPCWSTR outputFile,
UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
+ UINT64 profilerSamplingRateInNanoseconds,
EventPipeProviderConfiguration *pProviders,
UINT32 numProviders)
{
UINT64 sessionID = 0;
+ // Invalid input!
+ if (circularBufferSizeInMB == 0 ||
+ profilerSamplingRateInNanoseconds == 0 ||
+ numProviders == 0 ||
+ pProviders == nullptr)
+ {
+ return 0;
+ }
+
BEGIN_QCALL;
{
sessionID = EventPipe::Enable(
circularBufferSizeInMB,
profilerSamplingRateInNanoseconds,
pProviders,
- numProviders);
+ numProviders,
+ outputFile != NULL ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
+ nullptr);
}
END_QCALL;
static UINT64 QCALLTYPE Enable(
__in_z LPCWSTR outputFile,
UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
+ UINT64 profilerSamplingRateInNanoseconds,
EventPipeProviderConfiguration *pProviders,
UINT32 numProviders);
- //! TODO: Add a ListActiveSessions to get the live SessionID in order to Disable?
-
//!
//! Disables the specified session Id.
//!
// See the LICENSE file in the project root for more information.
#include "common.h"
+#include "fastserializer.h"
+#include "eventpipefile.h"
#include "eventpipeprotocolhelper.h"
+#include "eventpipesession.h"
#include "diagnosticsipc.h"
#include "diagnosticsprotocol.h"
#ifdef FEATURE_PERFTRACING
+static bool IsNullOrWhiteSpace(LPCWSTR value)
+{
+ if (value == nullptr)
+ return true;
+
+ while (*value)
+ {
+ if (!iswspace(*value))
+ return false;
+ ++value;
+ }
+ return true;
+}
+
bool EventPipeProtocolHelper::TryParseProviderConfiguration(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
{
// Picking an arbitrary upper bound,
LPCWSTR pProviderName = nullptr;
if (!TryParseString(bufferCursor, bufferLen, pProviderName))
return false;
- if (wcslen(pProviderName) == 0)
- return false; // TODO: Should we ignore these input?
+ if (IsNullOrWhiteSpace(pProviderName))
+ return false;
LPCWSTR pFilterData = nullptr; // This parameter is optional.
TryParseString(bufferCursor, bufferLen, pFilterData);
pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData);
}
- return true;
+ return (countConfigs > 0);
}
-void EventPipeProtocolHelper::EnableFileTracingEventHandler(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, 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;
- CQuickArray<EventPipeProviderConfiguration> providerConfigs;
-
- uint8_t *pBufferCursor = buffer;
- uint32_t bufferLen = nNumberOfBytesRead;
- if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
- !TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
- !TryParseProviderConfiguration(pBufferCursor, bufferLen, providerConfigs))
- {
- // TODO: error handling
- delete pStream;
- return;
- }
-
- 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
- }
-
- 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::DisableFileTracingEventHandler(IpcStream *pStream)
+void EventPipeProtocolHelper::StopTracing(IpcStream *pStream)
{
CONTRACTL
{
delete pStream;
}
-void EventPipeProtocolHelper::AttachTracingEventHandler(IpcStream *pStream)
+static bool TryParseCircularBufferSize(uint8_t *&bufferCursor, uint32_t &bufferLen, uint32_t &circularBufferSizeInMB)
+{
+ const bool CanParse = TryParse(bufferCursor, bufferLen, circularBufferSizeInMB);
+ return CanParse && (circularBufferSizeInMB > 0);
+}
+
+void EventPipeProtocolHelper::CollectTracing(IpcStream *pStream)
{
CONTRACTL
{
}
CONTRACTL_END;
+ if (pStream == nullptr)
+ return;
+
// TODO: Read within a loop.
uint8_t buffer[IpcStreamReadBufferSize]{};
uint32_t nNumberOfBytesRead = 0;
uint8_t *pBufferCursor = buffer;
uint32_t bufferLen = nNumberOfBytesRead;
- if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
- !TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
+ if (!TryParseCircularBufferSize(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
+ !TryParseString(pBufferCursor, bufferLen, strOutputPath) || // TODO: Remove. Currently ignored in this scenario.
!TryParseProviderConfiguration(pBufferCursor, bufferLen, providerConfigs))
{
// TODO: error handling
return;
}
- if (providerConfigs.Size() > 0)
- {
- EventPipe::Enable(
- pStream, // IPC stream
- circularBufferSizeInMB, // circularBufferSizeInMB
- DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
- providerConfigs.Ptr(), // pConfigs
- static_cast<uint32_t>(providerConfigs.Size())); // numConfigs
- }
+ auto sessionId = EventPipe::Enable(
+ nullptr, // strOutputPath (ignored in this scenario)
+ circularBufferSizeInMB, // circularBufferSizeInMB
+ DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
+ providerConfigs.Ptr(), // pConfigs
+ static_cast<uint32_t>(providerConfigs.Size()), // numConfigs
+ EventPipeSessionType::IpcStream, // EventPipeSessionType
+ pStream); // IpcStream
+
+ if (sessionId == 0)
+ delete pStream;
}
#endif // FEATURE_PERFTRACING
{
public:
// IPC event handlers.
- static void EnableFileTracingEventHandler(IpcStream *pStream);
- static void DisableFileTracingEventHandler(IpcStream *pStream);
-
- static void AttachTracingEventHandler(IpcStream *pStream);
+ static void StopTracing(IpcStream *pStream);
+ static void CollectTracing(IpcStream *pStream); // `dotnet-trace collect`
private:
const static uint32_t DefaultCircularBufferMB = 1024; // 1 GB
THROWS;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
+ PRECONDITION(circularBufferSizeInMB > 0);
+ PRECONDITION(numProviders > 0 && pProviders != nullptr);
}
CONTRACTL_END;
enum class EventPipeSessionType
{
- File,
- Streaming,
- IpcStream
+ File, // EventToFile
+ Streaming, // EventToEventListener
+ IpcStream // EventToIpc
};
class EventPipeSession
// As a result of work on V3 of Event Pipe (https://github.com/Microsoft/perfview/pull/532) it got removed
// if you need it, please use git to restore it
-IpcStreamWriter::IpcStreamWriter(IpcStream *pStream) : _pStream(pStream)
+IpcStreamWriter::IpcStreamWriter(uint64_t id, IpcStream *pStream) : _pStream(pStream)
{
CONTRACTL
{
PRECONDITION(_pStream != nullptr);
}
CONTRACTL_END;
+
+ if (_pStream == nullptr)
+ return;
+
+ uint32_t nBytesWritten = 0;
+ bool fSuccess = _pStream->Write(&id, sizeof(id), nBytesWritten);
+ if (!fSuccess)
+ {
+ delete _pStream;
+ _pStream = nullptr;
+ }
}
IpcStreamWriter::~IpcStreamWriter()
if (_pStream == nullptr)
return false;
+ if (lpBuffer == nullptr || nBytesToWrite == 0)
+ return false;
return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten);
}
class IpcStreamWriter final : public StreamWriter
{
public:
- IpcStreamWriter(IpcStream *pStream);
+ IpcStreamWriter(uint64_t id, IpcStream *pStream);
~IpcStreamWriter();
bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
private:
- IpcStream *const _pStream;
+ IpcStream *_pStream;
};
//!