<XunitPackageVersion>2.2.0-beta2-build3300</XunitPackageVersion>
<XunitConsoleNetcorePackageVersion>1.0.2-prerelease-00177</XunitConsoleNetcorePackageVersion>
<XunitPerformanceApiPackageVersion>1.0.0-beta-build0015</XunitPerformanceApiPackageVersion>
- <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.2</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
+ <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.4</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
<VCRuntimeVersion>1.2.0</VCRuntimeVersion>
</PropertyGroup>
eventpipeconfiguration.cpp
eventpipeevent.cpp
eventpipeeventinstance.cpp
+ eventpipeblock.cpp
eventpipefile.cpp
eventpipejsonfile.cpp
eventpipeprovider.cpp
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeblock.h"
+#include "eventpipeeventinstance.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeBlock::EventPipeBlock(unsigned int maxBlockSize)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pBlock = new (nothrow) BYTE[maxBlockSize];
+ if (m_pBlock == NULL)
+ {
+ return;
+ }
+
+ memset(m_pBlock, 0, maxBlockSize);
+ m_pWritePointer = m_pBlock;
+ m_pEndOfTheBuffer = m_pBlock + maxBlockSize;
+}
+
+EventPipeBlock::~EventPipeBlock()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_pBlock != NULL)
+ {
+ m_pEndOfTheBuffer = NULL;
+ m_pWritePointer = NULL;
+ delete[] m_pBlock;
+ m_pBlock = NULL;
+ }
+}
+
+bool EventPipeBlock::WriteEvent(EventPipeEventInstance &instance)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pBlock == NULL)
+ {
+ return false;
+ }
+
+ unsigned int totalSize = instance.GetAlignedTotalSize();
+ if (m_pWritePointer + totalSize >= m_pEndOfTheBuffer)
+ {
+ return false;
+ }
+
+ BYTE* alignedEnd = m_pWritePointer + totalSize + sizeof(totalSize);
+
+ memcpy(m_pWritePointer, &totalSize, sizeof(totalSize));
+ m_pWritePointer += sizeof(totalSize);
+
+ unsigned int metadataId = instance.GetMetadataId();
+ memcpy(m_pWritePointer, &metadataId, sizeof(metadataId));
+ m_pWritePointer += sizeof(metadataId);
+
+ DWORD threadId = instance.GetThreadId();
+ memcpy(m_pWritePointer, &threadId, sizeof(threadId));
+ m_pWritePointer += sizeof(threadId);
+
+ const LARGE_INTEGER* timeStamp = instance.GetTimeStamp();
+ memcpy(m_pWritePointer, timeStamp, sizeof(*timeStamp));
+ m_pWritePointer += sizeof(*timeStamp);
+
+ const GUID* activityId = instance.GetActivityId();
+ memcpy(m_pWritePointer, activityId, sizeof(*activityId));
+ m_pWritePointer += sizeof(*activityId);
+
+ const GUID* relatedActivityId = instance.GetRelatedActivityId();
+ memcpy(m_pWritePointer, relatedActivityId, sizeof(*relatedActivityId));
+ m_pWritePointer += sizeof(*relatedActivityId);
+
+ unsigned int dataLength = instance.GetDataLength();
+ memcpy(m_pWritePointer, &dataLength, sizeof(dataLength));
+ m_pWritePointer += sizeof(dataLength);
+
+ if (dataLength > 0)
+ {
+ memcpy(m_pWritePointer, instance.GetData(), dataLength);
+ m_pWritePointer += dataLength;
+ }
+
+ unsigned int stackSize = instance.GetStackSize();
+ memcpy(m_pWritePointer, &stackSize, sizeof(stackSize));
+ m_pWritePointer += sizeof(stackSize);
+
+ if (stackSize > 0)
+ {
+ memcpy(m_pWritePointer, instance.GetStack(), stackSize);
+ m_pWritePointer += stackSize;
+ }
+
+ while (m_pWritePointer < alignedEnd)
+ {
+ *m_pWritePointer++ = (BYTE)0; // put padding at the end to get 4 bytes alignment of the payload
+ }
+
+ return true;
+}
+
+void EventPipeBlock::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pBlock == NULL)
+ {
+ return;
+ }
+
+ memset(m_pBlock, 0, GetSize());
+ m_pWritePointer = m_pBlock;
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_BLOCK_H__
+#define __EVENTPIPE_BLOCK_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipeeventinstance.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+class EventPipeBlock : public FastSerializableObject
+{
+ 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()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return "EventBlock";
+ }
+
+ 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);
+ }
+
+ private:
+ BYTE *m_pBlock;
+ BYTE *m_pWritePointer;
+ BYTE *m_pEndOfTheBuffer;
+
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pBlock == NULL)
+ {
+ return 0;
+ }
+
+ return (unsigned int)(m_pEndOfTheBuffer - m_pBlock);
+ }
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_BLOCK_H__
}
// Save the most recent event timestamp.
- m_mostRecentTimeStamp = pInstance->GetTimeStamp();
+ m_mostRecentTimeStamp = *pInstance->GetTimeStamp();
}
EX_CATCH
// We have a pointer within the bounds of the buffer.
// Find the next event by skipping the current event with it's data payload immediately after the instance.
- pNextInstance = (EventPipeEventInstance *)(pEvent->GetData() + pEvent->GetLength());
+ pNextInstance = (EventPipeEventInstance *)(pEvent->GetData() + pEvent->GetDataLength());
// Check to see if we've reached the end of the written portion of the buffer.
if((BYTE*)pNextInstance >= m_pCurrent)
}
// Ensure that the timestamp is valid. The buffer is zero'd before use, so a zero timestamp is invalid.
- LARGE_INTEGER nextTimeStamp = pNextInstance->GetTimeStamp();
+ LARGE_INTEGER nextTimeStamp = *pNextInstance->GetTimeStamp();
if(nextTimeStamp.QuadPart == 0)
{
return NULL;
_ASSERTE(pInstance->EnsureConsistency());
// Validate that payload and length match.
- _ASSERTE((pInstance->GetData() != NULL && pInstance->GetLength() > 0) || (pInstance->GetData() != NULL && pInstance->GetLength() == 0));
+ _ASSERTE((pInstance->GetData() != NULL && pInstance->GetDataLength() > 0) || (pInstance->GetData() != NULL && pInstance->GetDataLength() == 0));
// Skip the event.
- ptr += sizeof(*pInstance) + pInstance->GetLength();
+ ptr += sizeof(*pInstance) + pInstance->GetDataLength();
}
// When we're done walking the filled portion of the buffer,
{
// 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;
Enable(pSession);
}
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance)
+EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
{
CONTRACTL
{
CONTRACTL_END;
// The payload of the event should contain:
+ // - Metadata ID
// - GUID ProviderID.
- // - unsigned int EventID.
- // - unsigned int EventVersion.
// - Optional event description payload.
// Calculate the size of the event.
EventPipeEvent &sourceEvent = *sourceInstance.GetEvent();
const SString &providerName = sourceEvent.GetProvider()->GetProviderName();
- unsigned int eventID = sourceEvent.GetEventID();
- unsigned int eventVersion = sourceEvent.GetEventVersion();
BYTE *pPayloadData = sourceEvent.GetMetadata();
unsigned int payloadLength = sourceEvent.GetMetadataLength();
unsigned int providerNameLength = (providerName.GetCount() + 1) * sizeof(WCHAR);
- unsigned int instancePayloadSize = providerNameLength + sizeof(eventID) + sizeof(eventVersion) + sizeof(payloadLength) + payloadLength;
+ unsigned int instancePayloadSize = sizeof(metadataId) + providerNameLength + payloadLength;
// Allocate the payload.
BYTE *pInstancePayload = new BYTE[instancePayloadSize];
// Fill the buffer with the payload.
BYTE *currentPtr = pInstancePayload;
- // Write the provider ID.
+ memcpy(currentPtr, &metadataId, sizeof(metadataId));
+ currentPtr += sizeof(metadataId);
+
memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength);
currentPtr += providerNameLength;
- // Write the event name as null-terminated unicode.
- memcpy(currentPtr, &eventID, sizeof(eventID));
- currentPtr += sizeof(eventID);
-
- // Write the event version.
- memcpy(currentPtr, &eventVersion, sizeof(eventVersion));
- currentPtr += sizeof(eventVersion);
-
- // Write the size of the metadata.
- memcpy(currentPtr, &payloadLength, sizeof(payloadLength));
- currentPtr += sizeof(payloadLength);
-
// Write the incoming payload data.
memcpy(currentPtr, pPayloadData, payloadLength);
// Set the timestamp to match the source event, because the metadata event
// will be emitted right before the source event.
- pInstance->SetTimeStamp(sourceInstance.GetTimeStamp());
+ pInstance->SetTimeStamp(*sourceInstance.GetTimeStamp());
return pInstance;
}
void EnableRundown(EventPipeSession *pSession);
// Get the event used to write metadata to the event stream.
- EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance);
+ EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId);
// Delete deferred providers.
void DeleteDeferredProviders();
}
else
{
- m_pMetadata = NULL;
- m_metadataLength = 0;
+ // if metadata is not provided, we have to build the minimum version. It's required by the serialization contract
+ m_pMetadata = BuildMinimumMetadata();
+ m_metadataLength = GetMinimumMetadataLength();
}
}
}
}
+unsigned int EventPipeEvent::GetMinimumMetadataLength()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ unsigned int minimumMetadataLength =
+ sizeof(m_eventID) +
+ (SString::Empty().GetCount() + 1) * sizeof(WCHAR) + // size of empty unicode string
+ sizeof(m_keywords) +
+ sizeof(m_eventVersion) +
+ sizeof(m_level) +
+ sizeof(unsigned int); // parameter count
+
+ return minimumMetadataLength;
+}
+
+BYTE *EventPipeEvent::BuildMinimumMetadata()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ unsigned int minmumMetadataLength = GetMinimumMetadataLength();
+
+ BYTE *minmumMetadata = new BYTE[minmumMetadataLength];
+ BYTE *currentPtr = minmumMetadata;
+
+ // the order of fields is defined in coreclr\src\mscorlib\shared\System\Diagnostics\Tracing\EventSource.cs DefineEventPipeEvents method
+ memcpy(currentPtr, &m_eventID, sizeof(m_eventID));
+ currentPtr += sizeof(m_eventID);
+
+ SString eventName = SString::Empty();
+ unsigned int eventNameSize = (eventName.GetCount() + 1) * sizeof(WCHAR);
+ memcpy(currentPtr, (BYTE*)eventName.GetUnicode(), eventNameSize);
+ currentPtr += eventNameSize;
+
+ memcpy(currentPtr, &m_keywords, sizeof(m_keywords));
+ currentPtr += sizeof(m_keywords);
+
+ memcpy(currentPtr, &m_eventVersion, sizeof(m_eventVersion));
+ currentPtr += sizeof(m_eventVersion);
+
+ memcpy(currentPtr, &m_level, sizeof(m_level));
+ currentPtr += sizeof(m_level);
+
+ unsigned int noParameters = 0;
+ memcpy(currentPtr, &noParameters, sizeof(noParameters));
+ currentPtr += sizeof(noParameters);
+
+ return minmumMetadata;
+}
+
EventPipeProvider* EventPipeEvent::GetProvider() const
{
LIMITED_METHOD_CONTRACT;
// True if the event is currently enabled.
bool IsEnabled() const;
- // Get metadata
BYTE *GetMetadata() const;
- // Get metadata length
unsigned int GetMetadataLength() const;
+
+ private:
+ // used when Metadata is not provided
+ BYTE *BuildMinimumMetadata();
+
+ unsigned int GetMinimumMetadataLength();
};
#endif // FEATURE_PERFTRACING
#endif // _DEBUG
}
-StackContents* EventPipeEventInstance::GetStack()
+unsigned int EventPipeEventInstance::GetAlignedTotalSize() const
{
- LIMITED_METHOD_CONTRACT;
-
- return &m_stackContents;
-}
-
-EventPipeEvent* EventPipeEventInstance::GetEvent() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_pEvent;
-}
-
-LARGE_INTEGER EventPipeEventInstance::GetTimeStamp() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_timeStamp;
-}
-
-BYTE* EventPipeEventInstance::GetData() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_pData;
-}
-
-unsigned int EventPipeEventInstance::GetLength() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_dataLength;
-}
-
-void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel)
-{
- CONTRACTL
+ CONTRACT(unsigned int)
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
+ POSTCONDITION(RETVAL % ALIGNMENT_SIZE == 0);
}
- CONTRACTL_END;
-
-#ifdef EVENTPIPE_EVENT_MARKER
- // Useful for diagnosing serialization bugs.
- const unsigned int value = 0xDEADBEEF;
- pSerializer->WriteBuffer((BYTE*)&value, sizeof(value));
-#endif
+ CONTRACT_END;
// Calculate the size of the total payload so that it can be written to the file.
unsigned int payloadLength =
- sizeof(metadataLabel) +
+ sizeof(m_metadataId) + // Metadata ID
sizeof(m_threadID) + // Thread ID
sizeof(m_timeStamp) + // TimeStamp
sizeof(m_activityId) + // Activity ID
sizeof(unsigned int) + // Prepended stack payload size in bytes
m_stackContents.GetSize(); // Stack payload size
- // Write the size of the event to the file.
- pSerializer->WriteBuffer((BYTE*)&payloadLength, sizeof(payloadLength));
-
- // Write the metadata label.
- pSerializer->WriteBuffer((BYTE*)&metadataLabel, sizeof(metadataLabel));
-
- // Write the thread ID.
- pSerializer->WriteBuffer((BYTE*)&m_threadID, sizeof(m_threadID));
-
- // Write the timestamp.
- pSerializer->WriteBuffer((BYTE*)&m_timeStamp, sizeof(m_timeStamp));
-
- // Write the activity id.
- pSerializer->WriteBuffer((BYTE*)&m_activityId, sizeof(m_activityId));
-
- // Write the related activity id.
- pSerializer->WriteBuffer((BYTE*)&m_relatedActivityId, sizeof(m_relatedActivityId));
-
- // Write the data payload size.
- pSerializer->WriteBuffer((BYTE*)&m_dataLength, sizeof(m_dataLength));
-
- // Write the event data payload.
- if(m_dataLength > 0)
+ // round up to ALIGNMENT_SIZE bytes
+ if (payloadLength % ALIGNMENT_SIZE != 0)
{
- pSerializer->WriteBuffer(m_pData, m_dataLength);
+ payloadLength += ALIGNMENT_SIZE - (payloadLength % ALIGNMENT_SIZE);
}
- // Write the size of the stack in bytes.
- unsigned int stackSize = m_stackContents.GetSize();
- pSerializer->WriteBuffer((BYTE*)&stackSize, sizeof(stackSize));
-
- // Write the stack if present.
- if(stackSize > 0)
- {
- pSerializer->WriteBuffer(m_stackContents.GetPointer(), stackSize);
- }
+ RETURN payloadLength;
}
#ifdef _DEBUG
#include "eventpipe.h"
#include "eventpipeevent.h"
#include "eventpipesession.h"
+#include "eventpipeblock.h"
#include "fastserializableobject.h"
#include "fastserializer.h"
EventPipeEventInstance(EventPipeSession &session, EventPipeEvent &event, DWORD threadID, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId);
- // Get the event associated with this instance.
- EventPipeEvent* GetEvent() const;
+ StackContents* GetStack()
+ {
+ LIMITED_METHOD_CONTRACT;
- // Get the stack contents object to either read or write to it.
- StackContents* GetStack();
+ return &m_stackContents;
+ }
- // Get the timestamp.
- LARGE_INTEGER GetTimeStamp() const;
+ EventPipeEvent* GetEvent() const
+ {
+ LIMITED_METHOD_CONTRACT;
- // Get a pointer to the data payload.
- BYTE* GetData() const;
+ return m_pEvent;
+ }
- // Get the length of the data.
- unsigned int GetLength() const;
+ const LARGE_INTEGER* const GetTimeStamp() const
+ {
+ LIMITED_METHOD_CONTRACT;
- // Serialize this object using FastSerialization.
- void FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel);
+ return &m_timeStamp;
+ }
+
+ unsigned int GetMetadataId() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_metadataId;
+ }
+
+ void SetMetadataId(unsigned int metadataId)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_metadataId = metadataId;
+ }
+
+ DWORD GetThreadId() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_threadID;
+ }
+
+ const GUID* const GetActivityId() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return &m_activityId;
+ }
+
+ const GUID* const GetRelatedActivityId() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return &m_relatedActivityId;
+ }
+
+ const BYTE* const GetData() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pData;
+ }
+
+ unsigned int GetDataLength() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_dataLength;
+ }
+
+ unsigned int GetStackSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_stackContents.GetSize();
+ }
+
+ unsigned int GetAlignedTotalSize() const;
#ifdef _DEBUG
// Serialize this event to the JSON file.
#endif // _DEBUG
EventPipeEvent *m_pEvent;
+ unsigned int m_metadataId;
DWORD m_threadID;
LARGE_INTEGER m_timeStamp;
GUID m_activityId;
#include "common.h"
#include "eventpipebuffer.h"
+#include "eventpipeblock.h"
#include "eventpipeconfiguration.h"
#include "eventpipefile.h"
+#include "sampleprofiler.h"
#ifdef FEATURE_PERFTRACING
}
CONTRACTL_END;
- SetObjectVersion(2);
+ SetObjectVersion(3);
SetMinReaderVersion(0);
- m_pSerializer = new FastSerializer(outputFilePath, *this);
- m_serializationLock.Init(LOCK_TYPE_DEFAULT);
- m_pMetadataLabels = new MapSHashWithRemove<EventPipeEvent*, StreamLabel>();
+ m_pBlock = new EventPipeBlock(100 * 1024);
#ifdef _DEBUG
m_lockOnWrite = lockOnWrite;
QueryPerformanceCounter(&m_fileOpenTimeStamp);
QueryPerformanceFrequency(&m_timeStampFrequency);
- // Write a forward reference to the beginning of the event stream.
- // This also allows readers to know where the event stream ends and skip it if needed.
- m_beginEventsForwardReferenceIndex = m_pSerializer->AllocateForwardReference();
- m_pSerializer->WriteForwardReference(m_beginEventsForwardReferenceIndex);
+ m_pointerSize = TARGET_POINTER_SIZE;
+
+ m_currentProcessId = GetCurrentProcessId();
- // Write the header information into the file.
+ SYSTEM_INFO sysinfo = {};
+ GetSystemInfo(&sysinfo);
+ m_numberOfProcessors = sysinfo.dwNumberOfProcessors;
- // Write the current date and time.
- m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime));
+ m_samplingRateInNs = SampleProfiler::GetSamplingRate();
+
+ m_pSerializer = new FastSerializer(outputFilePath); // it creates the file stream and writes the header
+ m_serializationLock.Init(LOCK_TYPE_DEFAULT);
+ m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>();
- // Write FileOpenTimeStamp
- m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp));
+ m_metadataIdCounter = 0; // we start with 0, it's always gets incremented by generator so the first id will be 1, as specified in the docs
- // Write ClockFrequency
- m_pSerializer->WriteBuffer((BYTE*)&m_timeStampFrequency, sizeof(m_timeStampFrequency));
+ m_pSerializer->WriteObject(this); // this is the first object in the file
}
EventPipeFile::~EventPipeFile()
}
CONTRACTL_END;
- // Mark the end of the event stream.
- StreamLabel currentLabel = m_pSerializer->GetStreamLabel();
+ if (m_pBlock != NULL && m_pSerializer != NULL)
+ {
+ WriteEnd();
+ }
- // Define the event start forward reference.
- m_pSerializer->DefineForwardReference(m_beginEventsForwardReferenceIndex, currentLabel);
+ if (m_pBlock != NULL)
+ {
+ delete(m_pBlock);
+ m_pBlock = NULL;
+ }
- // Close the serializer.
if(m_pSerializer != NULL)
{
delete(m_pSerializer);
}
CONTRACTL_END;
+ // Check to see if we've seen this event type before.
+ // If not, then write the event metadata to the event stream first.
+ unsigned int metadataId = GetMetadataId(*instance.GetEvent());
+ if(metadataId == 0)
+ {
+ metadataId = GenerateMetadataId();
+
+ EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId);
+
+ WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event.
+
+ SaveMetadataId(*instance.GetEvent(), metadataId);
+
+ delete[] (pMetadataInstance->GetData());
+ delete (pMetadataInstance);
+ }
+
+ WriteToBlock(instance, metadataId);
+}
+
+void EventPipeFile::WriteEnd()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pSerializer->WriteObject(m_pBlock); // we write current block to the disk, whether it's full or not
+
+ m_pBlock->Clear();
+
+ // "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read."
+ // see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more
+ m_pSerializer->WriteTag(FastSerializerTags::NullReference);
+}
+
+void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ instance.SetMetadataId(metadataId);
+
+ if (m_pBlock->WriteEvent(instance))
+ {
+ return; // the block is not full, we added the event and continue
+ }
+
#ifdef _DEBUG
- if(m_lockOnWrite)
+ if (m_lockOnWrite)
{
// Take the serialization lock.
// This is used for synchronous file writes.
}
#endif // _DEBUG
- // Check to see if we've seen this event type before.
- // If not, then write the event metadata to the event stream first.
- StreamLabel metadataLabel = GetMetadataLabel(*instance.GetEvent());
- if(metadataLabel == 0)
- {
- EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance);
+ // 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
+ m_pSerializer->WriteObject(m_pBlock);
- metadataLabel = m_pSerializer->GetStreamLabel();
- pMetadataInstance->FastSerialize(m_pSerializer, (StreamLabel)0); // 0 breaks recursion and represents the metadata event.
+ m_pBlock->Clear();
- SaveMetadataLabel(*instance.GetEvent(), metadataLabel);
+ bool result = m_pBlock->WriteEvent(instance);
- delete[] (pMetadataInstance->GetData());
- delete (pMetadataInstance);
+ _ASSERTE(result == true); // we should never fail to add event to a clear block (if we do the max size is too small)
+}
+
+unsigned int EventPipeFile::GenerateMetadataId()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
}
+ CONTRACTL_END;
- // Write the event to the stream.
- instance.FastSerialize(m_pSerializer, metadataLabel);
+ // PAL does not support 32 bit InterlockedIncrement, so we are using the LONG version and cast to int
+ // https://github.com/dotnet/coreclr/blob/master/src/pal/inc/pal.h#L4159
+ // it's ok because the metadataId will never be bigger than 32 bit
+ return (unsigned int)InterlockedIncrement(&m_metadataIdCounter);
}
-StreamLabel EventPipeFile::GetMetadataLabel(EventPipeEvent &event)
+unsigned int EventPipeFile::GetMetadataId(EventPipeEvent &event)
{
CONTRACTL
{
}
CONTRACTL_END;
- StreamLabel outLabel;
- if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ unsigned int metadataId;
+ if(m_pMetadataIds->Lookup(&event, &metadataId))
{
- _ASSERTE(outLabel != 0);
- return outLabel;
+ _ASSERTE(metadataId != 0);
+ return metadataId;
}
return 0;
}
-void EventPipeFile::SaveMetadataLabel(EventPipeEvent &event, StreamLabel label)
+void EventPipeFile::SaveMetadataId(EventPipeEvent &event, unsigned int metadataId)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION(label > 0);
+ PRECONDITION(metadataId > 0);
}
CONTRACTL_END;
// If a pre-existing metadata label exists, remove it.
- StreamLabel outLabel;
- if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ unsigned int oldId;
+ if(m_pMetadataIds->Lookup(&event, &oldId))
{
- m_pMetadataLabels->Remove(&event);
+ m_pMetadataIds->Remove(&event);
}
// Add the metadata label.
- m_pMetadataLabels->Add(&event, label);
+ m_pMetadataIds->Add(&event, metadataId);
}
#endif // FEATURE_PERFTRACING
#ifdef FEATURE_PERFTRACING
#include "eventpipe.h"
+#include "eventpipeblock.h"
#include "eventpipeeventinstance.h"
#include "fastserializableobject.h"
#include "fastserializer.h"
);
~EventPipeFile();
- // Write an event to the file.
void WriteEvent(EventPipeEventInstance &instance);
- // Serialize this object.
- // Not supported - this is the entry object for the trace,
- // which means that the contents hasn't yet been created.
- void FastSerialize(FastSerializer *pSerializer)
+ void WriteEnd();
+
+ const char* GetTypeName()
{
LIMITED_METHOD_CONTRACT;
- _ASSERTE(!"This function should never be called!");
+ return "Trace";
}
- // Get the type name of this object.
- const char* GetTypeName()
+ void FastSerialize(FastSerializer *pSerializer)
{
- LIMITED_METHOD_CONTRACT;
- return "Microsoft.DotNet.Runtime.EventPipeFile";
+ 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));
}
private:
- // Get the metadata address in the file for an event.
- // The return value can be written into the file as a back-pointer to the event metadata.
- StreamLabel GetMetadataLabel(EventPipeEvent &event);
+ unsigned int GenerateMetadataId();
+
+ unsigned int GetMetadataId(EventPipeEvent &event);
- // Save the metadata address in the file for an event.
- void SaveMetadataLabel(EventPipeEvent &event, StreamLabel label);
+ void SaveMetadataId(EventPipeEvent &event, unsigned int metadataId);
+
+ void WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId);
// The object responsible for serialization.
FastSerializer *m_pSerializer;
+ EventPipeBlock *m_pBlock;
+
// The system time when the file was opened.
SYSTEMTIME m_fileOpenSystemTime;
// The frequency of the timestamps used for this file.
LARGE_INTEGER m_timeStampFrequency;
- // The forward reference index that marks the beginning of the event stream.
- unsigned int m_beginEventsForwardReferenceIndex;
+ unsigned int m_pointerSize;
+
+ unsigned int m_currentProcessId;
+
+ unsigned int m_numberOfProcessors;
+
+ 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;
// Hashtable of metadata labels.
- MapSHashWithRemove<EventPipeEvent*, StreamLabel> *m_pMetadataLabels;
+ MapSHashWithRemove<EventPipeEvent*, unsigned int> *m_pMetadataIds;
+
+ Volatile<LONG> m_metadataIdCounter;
#ifdef _DEBUG
bool m_lockOnWrite;
#ifdef FEATURE_PERFTRACING
-FastSerializer::FastSerializer(SString &outputFilePath, FastSerializableObject &object)
+// Event Pipe has previously implemented a feature called "forward references"
+// As a result of work on V3 of Event Pipe (https://github.com/Microsoft/perfview/pull/532) it got removed
+// if you need it, please use git to restore it
+
+FastSerializer::FastSerializer(SString &outputFilePath)
{
CONTRACTL
{
CONTRACTL_END;
m_writeErrorEncountered = false;
- m_pEntryObject = &object;
m_currentPos = 0;
- m_nextForwardReference = 0;
m_pFileStream = new CFileStream();
if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
{
return;
}
- // Write the file header.
WriteFileHeader();
-
- // Write the entry object.
- WriteEntryObject();
}
FastSerializer::~FastSerializer()
}
CONTRACTL_END;
- // Write the end of the entry object.
- WriteTag(FastSerializerTags::EndObject);
-
- // Write forward reference table.
- StreamLabel forwardReferenceLabel = WriteForwardReferenceTable();
-
- // Write trailer.
- WriteTrailer(forwardReferenceLabel);
-
if(m_pFileStream != NULL)
{
delete(m_pFileStream);
{
CONTRACTL
{
- THROWS;
- GC_TRIGGERS;
+ NOTHROW;
+ GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(pObject != NULL);
}
CONTRACTL_END;
- // Write a BeginObject tag.
WriteTag(FastSerializerTags::BeginObject);
- // Write object begin tag.
WriteSerializationType(pObject);
// Ask the object to serialize itself using the current serializer.
pObject->FastSerialize(this);
- // Write object end tag.
WriteTag(FastSerializerTags::EndObject);
}
EX_END_CATCH(SwallowAllExceptions);
}
-void FastSerializer::WriteEntryObject()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- // Write begin entry object tag.
- WriteTag(FastSerializerTags::BeginObject);
-
- // Write the type information for the entry object.
- WriteSerializationType(m_pEntryObject);
-
- // The object is now initialized. Fields or other objects can now be written.
-}
-
-unsigned int FastSerializer::AllocateForwardReference()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- PRECONDITION(m_nextForwardReference < MaxForwardReferences);
- }
- CONTRACTL_END;
-
- // TODO: Handle failure.
-
- // Save the index.
- int index = m_nextForwardReference;
-
- // Allocate the forward reference and zero-fill it so that the reader
- // will know if it was not properly defined.
- m_forwardReferences[m_nextForwardReference++] = 0;
-
- return index;
-}
-
-void FastSerializer::DefineForwardReference(unsigned int index, StreamLabel value)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- PRECONDITION(index < MaxForwardReferences-1);
- }
- CONTRACTL_END;
-
- m_forwardReferences[index] = value;
-}
-
-void FastSerializer::WriteForwardReference(unsigned int index)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- PRECONDITION(index < MaxForwardReferences-1);
- }
- CONTRACTL_END;
-
- WriteBuffer((BYTE*)&index, sizeof(index));
-}
-
void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
{
CONTRACTL
{
NOTHROW;
- GC_TRIGGERS;
+ GC_NOTRIGGER;
MODE_PREEMPTIVE;
PRECONDITION(pObject != NULL);
}
// Write the SerializationType TypeName field.
const char *strTypeName = pObject->GetTypeName();
unsigned int length = (unsigned int)strlen(strTypeName);
+
WriteString(strTypeName, length);
// Write the EndObject tag.
CONTRACTL
{
NOTHROW;
- GC_TRIGGERS;
+ GC_NOTRIGGER;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
}
}
-
void FastSerializer::WriteFileHeader()
{
CONTRACTL
{
NOTHROW;
- GC_TRIGGERS;
+ GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
- const char *strSignature = "!FastSerialization.1";
+ const char *strSignature = "!FastSerialization.1"; // the consumer lib expects exactly the same string, it must not be changed
unsigned int length = (unsigned int)strlen(strSignature);
WriteString(strSignature, length);
}
CONTRACTL
{
NOTHROW;
- GC_TRIGGERS;
+ GC_NOTRIGGER;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
WriteBuffer((BYTE*) strContents, length);
}
-StreamLabel FastSerializer::WriteForwardReferenceTable()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- // Save the position of the start of the forward references table.
- StreamLabel current = GetStreamLabel();
-
- // Write the count of allocated references.
- WriteBuffer((BYTE*) &m_nextForwardReference, sizeof(m_nextForwardReference));
-
- // Write each of the allocated references.
- WriteBuffer((BYTE*) m_forwardReferences, sizeof(StreamLabel) * m_nextForwardReference);
-
- return current;
-}
-
-void FastSerializer::WriteTrailer(StreamLabel forwardReferencesTableStart)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- // Get the current location to mark the beginning of the trailer.
- StreamLabel current = GetStreamLabel();
-
- // Write the trailer, which contains the start of the forward references table.
- WriteBuffer((BYTE*) &forwardReferencesTableStart, sizeof(forwardReferencesTableStart));
-
- // Write the location of the trailer. This is the final piece of data written to the file,
- // so that it can be easily found by a reader that can seek to the end of the file.
- WriteBuffer((BYTE*) ¤t, sizeof(current));
-}
-
#endif // FEATURE_PERFTRACING
#ifndef __FASTSERIALIZER_H__
#define __FASTSERIALIZER_H__
+#define ALIGNMENT_SIZE 4
+
#ifdef FEATURE_PERFTRACING
#include "fastserializableobject.h"
typedef unsigned int StreamLabel;
+// the enumeration has a specific set of values to keep it compatible with consumer library
+// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295
enum class FastSerializerTags : BYTE
{
- Error, // To improve debugabilty, 0 is an illegal tag.
- NullReference, // Tag for a null object forwardReference.
- ObjectReference, // Followed by StreamLabel
- ForwardReference, // Followed by an index (32-bit integer) into the Forward forwardReference array and a Type object
- BeginObject, // Followed by Type object, object data, tagged EndObject
- BeginPrivateObject, // Like beginObject, but not placed in interning table on deserialiation
- EndObject, // Placed after an object to mark its end.
- ForwardDefinition, // Followed by a forward forwardReference index and an object definition (BeginObject)
- Byte,
+ Error = 0, // To improve debugabilty, 0 is an illegal tag.
+ NullReference = 1, // Tag for a null object forwardReference.
+ ObjectReference = 2, // Followed by StreamLabel
+ // 3 used to belong to ForwardReference, which got removed in V3
+ BeginObject = 4, // Followed by Type object, object data, tagged EndObject
+ BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation
+ EndObject = 6, // Placed after an object to mark its end.
+ // 7 used to belong to ForwardDefinition, which got removed in V3
+ Byte = 8,
Int16,
Int32,
Int64,
SkipRegion,
String,
- Limit, // Just past the last valid tag, used for asserts.
+ Blob,
+ Limit // Just past the last valid tag, used for asserts.
};
class FastSerializer
{
public:
- FastSerializer(SString &outputFilePath, FastSerializableObject &object);
+ FastSerializer(SString &outputFilePath);
~FastSerializer();
StreamLabel GetStreamLabel() const;
void WriteTag(FastSerializerTags tag, BYTE *payload = NULL, unsigned int payloadLength = 0);
void WriteString(const char *strContents, unsigned int length);
- unsigned int AllocateForwardReference();
- void DefineForwardReference(unsigned int index, StreamLabel value);
- void WriteForwardReference(unsigned int index);
+ size_t GetCurrentPosition() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_currentPos;
+ }
private:
- void WriteEntryObject();
void WriteSerializationType(FastSerializableObject *pObject);
void WriteFileHeader();
- StreamLabel WriteForwardReferenceTable();
- void WriteTrailer(StreamLabel forwardReferencesTableStart);
CFileStream *m_pFileStream;
bool m_writeErrorEncountered;
- FastSerializableObject *m_pEntryObject;
size_t m_currentPos;
-
- static const unsigned int MaxForwardReferences = 100;
- StreamLabel m_forwardReferences[MaxForwardReferences];
- unsigned int m_nextForwardReference;
};
#endif // FEATURE_PERFTRACING
// Set the sampling rate.
static void SetSamplingRate(unsigned long nanoseconds);
+ static unsigned long GetSamplingRate()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return s_samplingRateInNs;
+ }
+
private:
// Iterate through all managed threads and walk all stacks.
allocTickCount += 1;
// Some basic integrity checks
- // AssertEqual(data.TypeName, "System.Object"); https://github.com/Microsoft/perfview/issues/470
+ AssertEqual(data.TypeName, "System.Object");
AssertEqual(data.AllocationKind.ToString(), GCAllocationKind.Small.ToString());
AssertEqual(data.ProviderName, "Microsoft-Windows-DotNETRuntime");
AssertEqual(data.EventName, "GC/AllocationTick");