diff options
author | Victor "Nate" Graf <nategraf1@gmail.com> | 2017-08-24 15:13:49 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-24 15:13:49 -0700 |
commit | 421f9e7c64b05c71db6ef71791998c06249953f6 (patch) | |
tree | 3d416f3d85b892ff1b7c006de1453b1b5498bd2d | |
parent | 970c41b10cca6f0f4bc6c6524c6733ac2c5011ba (diff) | |
download | coreclr-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.cs | 4 | ||||
-rw-r--r-- | src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs | 21 | ||||
-rw-r--r-- | src/scripts/genEventPipe.py | 2 | ||||
-rw-r--r-- | src/vm/ecalllist.h | 1 | ||||
-rw-r--r-- | src/vm/eventpipe.cpp | 260 | ||||
-rw-r--r-- | src/vm/eventpipe.h | 81 | ||||
-rw-r--r-- | src/vm/eventpipebuffer.cpp | 11 | ||||
-rw-r--r-- | src/vm/eventpipebuffer.h | 3 | ||||
-rw-r--r-- | src/vm/eventpipebuffermanager.cpp | 9 | ||||
-rw-r--r-- | src/vm/eventpipebuffermanager.h | 3 | ||||
-rw-r--r-- | tests/src/tracing/eventpipesmoke/EventPipeSmoke.cs | 14 | ||||
-rw-r--r-- | tests/src/tracing/eventsourcesmoke/EventSourceSmoke.cs | 104 | ||||
-rw-r--r-- | tests/src/tracing/eventsourcesmoke/eventsourcesmoke.csproj | 31 |
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> |