summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor "Nate" Graf <nategraf1@gmail.com>2017-08-24 15:13:49 -0700
committerGitHub <noreply@github.com>2017-08-24 15:13:49 -0700
commit421f9e7c64b05c71db6ef71791998c06249953f6 (patch)
tree3d416f3d85b892ff1b7c006de1453b1b5498bd2d
parent970c41b10cca6f0f4bc6c6524c6733ac2c5011ba (diff)
downloadcoreclr-421f9e7c64b05c71db6ef71791998c06249953f6.tar.gz
coreclr-421f9e7c64b05c71db6ef71791998c06249953f6.tar.bz2
coreclr-421f9e7c64b05c71db6ef71791998c06249953f6.zip
Remove unnecessary buffer copy from EventPipe::WriteEvent pipeline (#13347)
* [WIP] Eliminate extra buffer copy with new api path * Copy blobs to a flat buffer is Rundown is on * Refactor to use payload class and dedupe code * Add contracts * Fix many small errors * Make names unambiguous * Add EventPipe::WriteEventBlob to ecalllist.h * Address code review * Add test and fix a buffer copy bug * Copy data instead of data pointer * Add optional output file arg to tests * Change failure return code * Renamed variables for clarity
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs4
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs21
-rw-r--r--src/scripts/genEventPipe.py2
-rw-r--r--src/vm/ecalllist.h1
-rw-r--r--src/vm/eventpipe.cpp260
-rw-r--r--src/vm/eventpipe.h81
-rw-r--r--src/vm/eventpipebuffer.cpp11
-rw-r--r--src/vm/eventpipebuffer.h3
-rw-r--r--src/vm/eventpipebuffermanager.cpp9
-rw-r--r--src/vm/eventpipebuffermanager.h3
-rw-r--r--tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs14
-rw-r--r--tests/src/tracing/eventsourcesmoke/EventSourceSmoke.cs104
-rw-r--r--tests/src/tracing/eventsourcesmoke/eventsourcesmoke.csproj31
13 files changed, 478 insertions, 66 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
index 2f6fdf62ef..0d216c38ab 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
@@ -171,5 +171,9 @@ namespace System.Diagnostics.Tracing
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
internal static extern unsafe void WriteEvent(IntPtr eventHandle, uint eventID, void* pData, uint length, Guid* activityId, Guid* relatedActivityId);
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ internal static extern unsafe void WriteEventData(IntPtr eventHandle, uint eventID, EventProvider.EventData** pEventData, uint dataCount, Guid* activityId, Guid* relatedActivityId);
}
}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
index d5bc4c2889..1c521bbafc 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
@@ -62,28 +62,11 @@ namespace System.Diagnostics.Tracing
{
if (userDataCount == 0)
{
- EventPipeInternal.WriteEvent(eventHandle, eventID, null, 0, activityId, relatedActivityId);
+ EventPipeInternal.WriteEventData(eventHandle, eventID, null, 0, activityId, relatedActivityId);
return 0;
}
- uint length = 0;
- for (int i = 0; i < userDataCount; i++)
- {
- length += userData[i].Size;
- }
-
- byte[] data = new byte[length];
- fixed (byte *pData = data)
- {
- uint offset = 0;
- for (int i = 0; i < userDataCount; i++)
- {
- byte * singleUserDataPtr = (byte *)(userData[i].Ptr);
- uint singleUserDataSize = userData[i].Size;
- WriteToBuffer(pData, length, ref offset, singleUserDataPtr, singleUserDataSize);
- }
- EventPipeInternal.WriteEvent(eventHandle, eventID, pData, length, activityId, relatedActivityId);
- }
+ EventPipeInternal.WriteEventData(eventHandle, eventID, &userData, (uint) userDataCount, activityId, relatedActivityId);
}
return 0;
}
diff --git a/src/scripts/genEventPipe.py b/src/scripts/genEventPipe.py
index 4c802acaf3..d5785d2799 100644
--- a/src/scripts/genEventPipe.py
+++ b/src/scripts/genEventPipe.py
@@ -110,7 +110,7 @@ def generateClrEventPipeWriteEventsImpl(
WriteEventImpl.append(
" EventPipe::WriteEvent(*EventPipeEvent" +
eventName +
- ", nullptr, 0);\n")
+ ", (BYTE*) nullptr, 0);\n")
WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index 114d2cf2fd..b76b81cb05 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -1245,6 +1245,7 @@ FCFuncStart(gEventPipeInternalFuncs)
QCFuncElement("DefineEvent", EventPipeInternal::DefineEvent)
QCFuncElement("DeleteProvider", EventPipeInternal::DeleteProvider)
QCFuncElement("WriteEvent", EventPipeInternal::WriteEvent)
+ QCFuncElement("WriteEventData", EventPipeInternal::WriteEventData)
FCFuncEnd()
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 9a94923493..d8d3791d59 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#include "clrtypes.h"
+#include "safemath.h"
#include "common.h"
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
@@ -38,6 +40,146 @@ extern "C" void InitProvidersAndEvents();
extern "C" 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
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pData = NULL;
+ m_pEventData = pEventData;
+ m_eventDataCount = eventDataCount;
+ m_allocatedData = false;
+
+ S_UINT32 tmp_size = S_UINT32(0);
+ for (unsigned int i=0; i<m_eventDataCount; i++)
+ {
+ tmp_size += S_UINT32(m_pEventData[i]->Size);
+ }
+
+ if (tmp_size.IsOverflow())
+ {
+ // If there is an overflow, drop the data and create an empty payload
+ m_pEventData = NULL;
+ m_eventDataCount = 0;
+ m_size = 0;
+ }
+ else
+ {
+ m_size = tmp_size.Value();
+ }
+}
+
+EventPipeEventPayload::~EventPipeEventPayload()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_allocatedData && m_pData != NULL)
+ {
+ delete[] m_pData;
+ m_pData = NULL;
+ }
+}
+
+void EventPipeEventPayload::Flatten()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_size > 0)
+ {
+ if (!IsFlattened())
+ {
+ BYTE* tmp_pData = new (nothrow) BYTE[m_size];
+ if (tmp_pData != NULL)
+ {
+ m_allocatedData = true;
+ CopyData(tmp_pData);
+ m_pData = tmp_pData;
+ }
+ }
+ }
+}
+
+void EventPipeEventPayload::CopyData(BYTE *pDst)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_size > 0)
+ {
+ if(IsFlattened())
+ {
+ memcpy(pDst, m_pData, m_size);
+ }
+
+ else if(m_pEventData != NULL)
+ {
+ unsigned int offset = 0;
+ for(unsigned int i=0; i<m_eventDataCount; i++)
+ {
+ memcpy(pDst + offset, (BYTE*)m_pEventData[i]->Ptr, m_pEventData[i]->Size);
+ offset += m_pEventData[i]->Size;
+ }
+ }
+ }
+}
+
+BYTE* EventPipeEventPayload::GetFlatData()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (!IsFlattened())
+ {
+ Flatten();
+ }
+ return m_pData;
+}
+
void EventPipe::Initialize()
{
STANDARD_VM_CONTRACT;
@@ -289,6 +431,34 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EventPipeEventPayload payload(pData, length);
+ EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
+}
+
+void EventPipe::WriteEvent(EventPipeEvent &event, EventData **pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EventPipeEventPayload payload(pEventData, eventDataCount);
+ EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
+}
+
+void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
PRECONDITION(s_pBufferManager != NULL);
}
CONTRACTL_END;
@@ -309,7 +479,7 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
{
- if(!s_pBufferManager->WriteEvent(pThread, event, pData, length, pActivityId, pRelatedActivityId))
+ if(!s_pBufferManager->WriteEvent(pThread, event, payload, pActivityId, pRelatedActivityId))
{
// This is used in DEBUG to make sure that we don't log an event synchronously that we didn't log to the buffer.
return;
@@ -317,19 +487,23 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
}
else if(s_pConfig->RundownEnabled())
{
- // Write synchronously to the file.
- // We're under lock and blocking the disabling thread.
- EventPipeEventInstance instance(
- event,
- pThread->GetOSThreadId(),
- pData,
- length,
- pActivityId,
- pRelatedActivityId);
-
- if(s_pFile != NULL)
+ BYTE *pData = payload.GetFlatData();
+ if (pData != NULL)
{
- s_pFile->WriteEvent(instance);
+ // Write synchronously to the file.
+ // We're under lock and blocking the disabling thread.
+ EventPipeEventInstance instance(
+ event,
+ pThread->GetOSThreadId(),
+ pData,
+ payload.GetSize(),
+ pActivityId,
+ pRelatedActivityId);
+
+ if(s_pFile != NULL)
+ {
+ s_pFile->WriteEvent(instance);
+ }
}
}
@@ -337,25 +511,29 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
{
GCX_PREEMP();
- // Create an instance of the event for the synchronous path.
- EventPipeEventInstance instance(
- event,
- pThread->GetOSThreadId(),
- pData,
- length,
- pActivityId,
- pRelatedActivityId);
-
- // Write to the EventPipeFile if it exists.
- if(s_pSyncFile != NULL)
+ BYTE *pData = payload.GetFlatData();
+ if (pData != NULL)
{
- s_pSyncFile->WriteEvent(instance);
- }
+ // Create an instance of the event for the synchronous path.
+ EventPipeEventInstance instance(
+ event,
+ pThread->GetOSThreadId(),
+ pData,
+ payload.GetSize(),
+ pActivityId,
+ pRelatedActivityId);
+
+ // Write to the EventPipeFile if it exists.
+ if(s_pSyncFile != NULL)
+ {
+ s_pSyncFile->WriteEvent(instance);
+ }
- // Write to the EventPipeJsonFile if it exists.
- if(s_pJsonFile != NULL)
- {
- s_pJsonFile->WriteEvent(instance);
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
+ }
}
}
#endif // _DEBUG
@@ -371,12 +549,14 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent
}
CONTRACTL_END;
+ EventPipeEventPayload payload(pData, length);
+
// Write the event to the thread's buffer.
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.
- if(!s_pBufferManager->WriteEvent(pSamplingThread, *pEvent, pData, length, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents))
+ if(!s_pBufferManager->WriteEvent(pSamplingThread, *pEvent, payload, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents))
{
// This is used in DEBUG to make sure that we don't log an event synchronously that we didn't log to the buffer.
return;
@@ -595,4 +775,22 @@ void QCALLTYPE EventPipeInternal::WriteEvent(
END_QCALL;
}
+void QCALLTYPE EventPipeInternal::WriteEventData(
+ INT_PTR eventHandle,
+ unsigned int eventID,
+ EventData **pEventData,
+ unsigned int 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;
+}
+
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index fa7d734280..6ea29ac63b 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -29,6 +29,69 @@ typedef void (*EventPipeCallback)(
void *FilterData,
void *CallbackContext);
+struct EventData
+{
+public:
+ unsigned long Ptr;
+ unsigned int Size;
+ unsigned int Reserved;
+};
+
+class EventPipeEventPayload
+{
+private:
+ BYTE *m_pData;
+ EventData **m_pEventData;
+ unsigned int m_eventDataCount;
+ unsigned int m_size;
+ bool m_allocatedData;
+
+ // If the data is stored only as an array of EventData objects, create a flat buffer and copy into it
+ void Flatten();
+
+public:
+ // Build this payload with a flat buffer inside
+ EventPipeEventPayload(BYTE *pData, unsigned int length);
+
+ // Build this payload to contain an array of EventData objects
+ EventPipeEventPayload(EventData **pEventData, unsigned int eventDataCount);
+
+ // If a buffer was allocated internally, delete it
+ ~EventPipeEventPayload();
+
+ // Copy the data (whether flat or array of objects) into a flat buffer at pDst
+ // Assumes that pDst points to an appropriatly sized buffer
+ void CopyData(BYTE *pDst);
+
+ // Get the flat formatted data in this payload
+ // This method will allocate a buffer if it does not already contain flattened data
+ // This method will return NULL on OOM if a buffer needed to be allocated
+ BYTE* GetFlatData();
+
+ // Return true is the data is stored in a flat buffer
+ bool IsFlattened() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pData != NULL;
+ }
+
+ // The the size of buffer needed to contain the stored data
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_size;
+ }
+
+ EventData** GetEventDataArray() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pEventData;
+ }
+};
+
class StackContents
{
private:
@@ -186,10 +249,14 @@ class EventPipe
// Delete a provider.
static void DeleteProvider(EventPipeProvider *pProvider);
- // Write out an event.
+ // 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);
@@ -199,6 +266,11 @@ class EventPipe
// Get the managed call stack for the specified thread.
static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
+ protected:
+
+ // The counterpart to WriteEvent which after the payload is constructed
+ static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+
private:
// Callback function for the stack walker. For each frame walked, this callback is invoked.
@@ -307,6 +379,13 @@ public:
void *pData,
unsigned int length,
LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static void QCALLTYPE WriteEventData(
+ INT_PTR eventHandle,
+ unsigned int eventID,
+ EventData **pEventData,
+ unsigned int eventDataCount,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffer.cpp b/src/vm/eventpipebuffer.cpp
index 00652c9fac..80b4a4f1b7 100644
--- a/src/vm/eventpipebuffer.cpp
+++ b/src/vm/eventpipebuffer.cpp
@@ -4,6 +4,7 @@
#include "common.h"
+#include "eventpipe.h"
#include "eventpipeeventinstance.h"
#include "eventpipebuffer.h"
@@ -46,7 +47,7 @@ EventPipeBuffer::~EventPipeBuffer()
}
}
-bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)
+bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)
{
CONTRACTL
{
@@ -58,7 +59,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
CONTRACTL_END;
// Calculate the size of the event.
- unsigned int eventSize = sizeof(EventPipeEventInstance) + dataLength;
+ unsigned int eventSize = sizeof(EventPipeEventInstance) + payload.GetSize();
// Make sure we have enough space to write the event.
if(m_pCurrent + eventSize >= m_pLimit)
@@ -77,7 +78,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
event,
pThread->GetOSThreadId(),
pDataDest,
- dataLength,
+ payload.GetSize(),
pActivityId,
pRelatedActivityId);
@@ -89,9 +90,9 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
}
// Write the event payload data to the buffer.
- if(dataLength > 0)
+ if(payload.GetSize() > 0)
{
- memcpy(pDataDest, pData, dataLength);
+ payload.CopyData(pDataDest);
}
// Save the most recent event timestamp.
diff --git a/src/vm/eventpipebuffer.h b/src/vm/eventpipebuffer.h
index f279a2865c..c96ad26609 100644
--- a/src/vm/eventpipebuffer.h
+++ b/src/vm/eventpipebuffer.h
@@ -7,6 +7,7 @@
#ifdef FEATURE_PERFTRACING
+#include "eventpipe.h"
#include "eventpipeevent.h"
#include "eventpipeeventinstance.h"
@@ -81,7 +82,7 @@ public:
// Returns:
// - true: The write succeeded.
// - false: The write failed. In this case, the buffer should be considered full.
- bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL);
+ bool WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL);
// Get the timestamp of the most recent event in the buffer.
LARGE_INTEGER GetMostRecentTimeStamp() const;
diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp
index 86a3e03c59..fa4c5e0b68 100644
--- a/src/vm/eventpipebuffermanager.cpp
+++ b/src/vm/eventpipebuffermanager.cpp
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
#include "common.h"
+#include "eventpipe.h"
#include "eventpipeconfiguration.h"
#include "eventpipebuffer.h"
#include "eventpipebuffermanager.h"
@@ -217,7 +218,7 @@ void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer)
}
}
-bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack)
+bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack)
{
CONTRACTL
{
@@ -276,7 +277,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
else
{
// Attempt to write the event to the buffer. If this fails, we should allocate a new buffer.
- allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pActivityId, pRelatedActivityId, pStack);
+ allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, payload, pActivityId, pRelatedActivityId, pStack);
}
}
@@ -290,7 +291,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
// However, the GC is waiting on this call to return so that it can make forward progress. Thus it is not safe
// to switch to preemptive mode here.
- unsigned int requestSize = sizeof(EventPipeEventInstance) + length;
+ unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize();
pBuffer = AllocateBufferForThread(pThread, requestSize);
}
@@ -299,7 +300,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
// This is the second time if this thread did have one or more buffers, but they were full.
if(allocNewBuffer && pBuffer != NULL)
{
- allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pActivityId, pRelatedActivityId, pStack);
+ allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, payload, pActivityId, pRelatedActivityId, pStack);
}
// Mark that the thread is no longer writing an event.
diff --git a/src/vm/eventpipebuffermanager.h b/src/vm/eventpipebuffermanager.h
index a53721b7b8..23e4e7f3e9 100644
--- a/src/vm/eventpipebuffermanager.h
+++ b/src/vm/eventpipebuffermanager.h
@@ -7,6 +7,7 @@
#ifdef FEATURE_PERFTRACING
+#include "eventpipe.h"
#include "eventpipefile.h"
#include "eventpipebuffer.h"
#include "spinlock.h"
@@ -67,7 +68,7 @@ public:
// This is because the thread that writes the events is not the same as the "event thread".
// An optional stack trace can be provided for sample profiler events.
// Otherwise, if a stack trace is needed, one will be automatically collected.
- bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL);
+ bool WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL);
// 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
diff --git a/tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs b/tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs
index 5bf11b8f78..78ab88aed9 100644
--- a/tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs
+++ b/tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs
@@ -10,11 +10,19 @@ namespace Tracing.Tests
{
private static int allocIterations = 10000;
private static int trivialSize = 0x100000;
- private static bool keepOutput = false;
static int Main(string[] args)
{
- string outputFilename = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".netperf";
+ bool keepOutput = false;
+ // Use the first arg as an output filename if there is one
+ string outputFilename = null;
+ if (args.Length >= 1) {
+ outputFilename = args[0];
+ keepOutput = true;
+ }
+ else {
+ outputFilename = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".netperf";
+ }
Console.WriteLine("\tStart: Enable tracing.");
TraceControl.EnableDefault(outputFilename);
@@ -49,7 +57,7 @@ namespace Tracing.Tests
System.IO.File.Delete(outputFilename);
}
- return pass ? 100 : 0;
+ return pass ? 100 : -1;
}
}
}
diff --git a/tests/src/tracing/eventsourcesmoke/EventSourceSmoke.cs b/tests/src/tracing/eventsourcesmoke/EventSourceSmoke.cs
new file mode 100644
index 0000000000..c3fe8366ab
--- /dev/null
+++ b/tests/src/tracing/eventsourcesmoke/EventSourceSmoke.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Threading.Tasks;
+using System.IO;
+using System.Diagnostics.Tracing;
+
+using Tracing.Tests.Common;
+
+namespace Tracing.Tests
+{
+ [EventSource(Name = "SimpleEventSource")]
+ class SimpleEventSource : EventSource
+ {
+ public SimpleEventSource() : base(true) { }
+
+ [Event(1)]
+ internal void Message(string msg) { this.WriteEvent(1, msg); }
+ }
+
+ class EventPipeSmoke
+ {
+ private static int messageIterations = 10000;
+ private static int trivialSize = 0x100000;
+
+ public static TraceConfiguration GetConfig(EventSource eventSource, string outputFile="default.netperf")
+ {
+ // Setup the configuration values.
+ uint circularBufferMB = 1024; // 1 GB
+ uint level = 5;//(uint)EventLevel.Informational;
+ TimeSpan profSampleDelay = TimeSpan.FromMilliseconds(1);
+
+ // Create a new instance of EventPipeConfiguration.
+ TraceConfiguration config = new TraceConfiguration(outputFile, circularBufferMB);
+ // Setup the provider values.
+ // Public provider.
+ string providerName = eventSource.Guid.ToString("D");
+ UInt64 keywords = 0xffffffffffffffff;
+
+ // Enable the provider.
+ config.EnableProvider(providerName, keywords, level);
+
+ // Set the sampling rate.
+ config.SetSamplingRate(profSampleDelay);
+
+ return config;
+ }
+
+ static int Main(string[] args)
+ {
+ bool keepOutput = false;
+ // Use the first arg as an output filename if there is one
+ string outputFilename = null;
+ if (args.Length >= 1) {
+ outputFilename = args[0];
+ keepOutput = true;
+ }
+ else {
+ outputFilename = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".netperf";
+ }
+
+ SimpleEventSource eventSource = new SimpleEventSource();
+
+ Console.WriteLine("\tStart: Enable tracing.");
+ TraceControl.Enable(GetConfig(eventSource, outputFilename));
+ Console.WriteLine("\tEnd: Enable tracing.\n");
+
+ Console.WriteLine("\tStart: Messaging.");
+ // Send messages
+ // Use random numbers and addition as a simple, human readble checksum
+ Random generator = new Random();
+ for(int i=0; i<messageIterations; i++)
+ {
+ int x = generator.Next(1,1000);
+ int y = generator.Next(1,1000);
+ string result = String.Format("{0} + {1} = {2}", x, y, x+y);
+
+ eventSource.Message(result);
+ }
+ Console.WriteLine("\tEnd: Messaging.\n");
+
+ Console.WriteLine("\tStart: Disable tracing.");
+ TraceControl.Disable();
+ Console.WriteLine("\tEnd: Disable tracing.\n");
+
+ FileInfo outputMeta = new FileInfo(outputFilename);
+ Console.WriteLine("\tCreated {0} bytes of data", outputMeta.Length);
+
+ bool pass = false;
+ if (outputMeta.Length > trivialSize){
+ pass = true;
+ }
+
+ if (keepOutput)
+ {
+ Console.WriteLine(String.Format("\tOutput file: {0}", outputFilename));
+ }
+ else
+ {
+ System.IO.File.Delete(outputFilename);
+ }
+
+ return pass ? 100 : -1;
+ }
+ }
+}
diff --git a/tests/src/tracing/eventsourcesmoke/eventsourcesmoke.csproj b/tests/src/tracing/eventsourcesmoke/eventsourcesmoke.csproj
new file mode 100644
index 0000000000..897ebfd983
--- /dev/null
+++ b/tests/src/tracing/eventsourcesmoke/eventsourcesmoke.csproj
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{8E3244CB-407F-4142-BAAB-E7A55901A5FA}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <CLRTestKind>BuildAndRun</CLRTestKind>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="EventSourceSmoke.cs" />
+ <ProjectReference Include="../common/common.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>