Event Pipe File V3 (#16107)
authorAdam Sitnik <adam.sitnik@gmail.com>
Fri, 2 Feb 2018 22:07:25 +0000 (23:07 +0100)
committerGitHub <noreply@github.com>
Fri, 2 Feb 2018 22:07:25 +0000 (23:07 +0100)
* write missing information to the event pipe file (pointer size to make it work fo x86)

* define where the events start, not only where they end

* include process Id in the event pipe file, bump the version so old consumers get clear error message

* write the missing EndObject tag to close the header

* include expected CPU sampling rate in the event pipe header file

* include keywords in V3 of EventPipe metadata, fixes #11934

* remove forward references

* entry object comes after the header and ends after it's data, before the event block objects

* introduce event block

* fix the GC contracts

* generate metadata ids

* end the file with null reference tag

* getting it work

* 4 byte alignment of serialized event data

* Revert "include keywords in V3 of EventPipe metadata, fixes #11934"

This reverts commit 98ef2f588e271f928fd051e96da526dc1e0f017c.

* remove event Id and event version from metadata buffer (it was duplicated with native code)

* increase the block size to be the same as buffer size

* Write the last event block to the file after disabling the event pipe, right after last events

* include the sife in itself

* the native part was supposed to not duplicate the event id and version, not manged

* ensure 4 byte alignment

* build metadata when it's not provided, so payload is never empty (no need to serialize length)

* this todo is no longer valid

* don't align everything, just the content of event block as suggested by @vancem

* improvements after code review

* update TraceEvent dependency, make the test verify new feature

* InterlockedIncrement(Int32) is not available for non-Windows OSes

* code improvements after Skype code review from @jorive

18 files changed:
dependencies.props
src/vm/CMakeLists.txt
src/vm/eventpipeblock.cpp [new file with mode: 0644]
src/vm/eventpipeblock.h [new file with mode: 0644]
src/vm/eventpipebuffer.cpp
src/vm/eventpipebuffermanager.cpp
src/vm/eventpipeconfiguration.cpp
src/vm/eventpipeconfiguration.h
src/vm/eventpipeevent.cpp
src/vm/eventpipeevent.h
src/vm/eventpipeeventinstance.cpp
src/vm/eventpipeeventinstance.h
src/vm/eventpipefile.cpp
src/vm/eventpipefile.h
src/vm/fastserializer.cpp
src/vm/fastserializer.h
src/vm/sampleprofiler.h
tests/src/tracing/eventpipetrace/EventPipeTrace.cs

index 03f9c9e..0dbef53 100644 (file)
@@ -38,7 +38,7 @@
     <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>
 
index cfa1b2a..f639679 100644 (file)
@@ -184,6 +184,7 @@ set(VM_SOURCES_WKS
     eventpipeconfiguration.cpp
     eventpipeevent.cpp
     eventpipeeventinstance.cpp
+    eventpipeblock.cpp
     eventpipefile.cpp
     eventpipejsonfile.cpp
     eventpipeprovider.cpp
diff --git a/src/vm/eventpipeblock.cpp b/src/vm/eventpipeblock.cpp
new file mode 100644 (file)
index 0000000..5992b87
--- /dev/null
@@ -0,0 +1,146 @@
+// 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
diff --git a/src/vm/eventpipeblock.h b/src/vm/eventpipeblock.h
new file mode 100644 (file)
index 0000000..30bd458
--- /dev/null
@@ -0,0 +1,92 @@
+// 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__
index 3ac2122..d88dd16 100644 (file)
@@ -97,7 +97,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, Eve
         }
 
         // Save the most recent event timestamp.
-        m_mostRecentTimeStamp = pInstance->GetTimeStamp();
+        m_mostRecentTimeStamp = *pInstance->GetTimeStamp();
 
     }
     EX_CATCH
@@ -174,7 +174,7 @@ EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent,
 
         // 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)
@@ -184,7 +184,7 @@ EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent,
     }
 
     // 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;
@@ -260,10 +260,10 @@ bool EventPipeBuffer::EnsureConsistency()
         _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,
index e93c439..5f09295 100644 (file)
@@ -430,7 +430,7 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
             {
                 // If it's the oldest event we've seen, then save it.
                 if((pOldestInstance == NULL) ||
-                   (pOldestInstance->GetTimeStamp().QuadPart > pNext->GetTimeStamp().QuadPart)) 
+                   (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart)) 
                 {
                     pOldestInstance = pNext;
                     pOldestContainingBuffer = pContainingBuffer;
index c8833dc..6206bbc 100644 (file)
@@ -448,7 +448,7 @@ void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession)
     Enable(pSession);
 }
 
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance)
+EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
 {
     CONTRACTL
     {
@@ -459,20 +459,17 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
     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];
@@ -480,22 +477,12 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
     // 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);
 
@@ -511,7 +498,7 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
 
     // 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;
 }
index 7e587cd..5ac68c2 100644 (file)
@@ -77,7 +77,7 @@ public:
     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();
index 1b2d4eb..3f77551 100644 (file)
@@ -33,8 +33,9 @@ EventPipeEvent::EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsi
     }
     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();
     }
 }
 
@@ -55,6 +56,61 @@ EventPipeEvent::~EventPipeEvent()
     }
 }
 
+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;
index c91c4ba..1ae0290 100644 (file)
@@ -75,11 +75,15 @@ private:
     // 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
index 51bd4ce..023b9b2 100644 (file)
@@ -65,60 +65,20 @@ EventPipeEventInstance::EventPipeEventInstance(
 #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
@@ -128,42 +88,13 @@ void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLa
         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
index 5298122..47f1b5c 100644 (file)
@@ -10,6 +10,7 @@
 #include "eventpipe.h"
 #include "eventpipeevent.h"
 #include "eventpipesession.h"
+#include "eventpipeblock.h"
 #include "fastserializableobject.h"
 #include "fastserializer.h"
 
@@ -22,23 +23,84 @@ public:
 
     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.
@@ -54,6 +116,7 @@ protected:
 #endif // _DEBUG
 
     EventPipeEvent *m_pEvent;
+    unsigned int m_metadataId;
     DWORD m_threadID;
     LARGE_INTEGER m_timeStamp;
     GUID m_activityId;
index ba4eb39..3346ee5 100644 (file)
@@ -4,8 +4,10 @@
 
 #include "common.h"
 #include "eventpipebuffer.h"
+#include "eventpipeblock.h"
 #include "eventpipeconfiguration.h"
 #include "eventpipefile.h"
+#include "sampleprofiler.h"
 
 #ifdef FEATURE_PERFTRACING
 
@@ -25,12 +27,10 @@ EventPipeFile::EventPipeFile(
     }
     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;
@@ -41,21 +41,23 @@ EventPipeFile::EventPipeFile(
     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()
@@ -68,13 +70,17 @@ 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);
@@ -92,8 +98,64 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
     }
     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.
@@ -102,27 +164,34 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
     }
 #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
     {
@@ -132,36 +201,36 @@ StreamLabel EventPipeFile::GetMetadataLabel(EventPipeEvent &event)
     }
     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
index 2f68535..48f0bb0 100644 (file)
@@ -9,6 +9,7 @@
 #ifdef FEATURE_PERFTRACING
 
 #include "eventpipe.h"
+#include "eventpipeblock.h"
 #include "eventpipeeventinstance.h"
 #include "fastserializableobject.h"
 #include "fastserializer.h"
@@ -25,37 +26,53 @@ class EventPipeFile : public FastSerializableObject
         );
         ~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;
 
@@ -65,15 +82,22 @@ class EventPipeFile : public FastSerializableObject
         // 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;
index 2e2939a..7a79b2f 100644 (file)
@@ -7,7 +7,11 @@
 
 #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
     {
@@ -18,9 +22,7 @@ FastSerializer::FastSerializer(SString &outputFilePath, FastSerializableObject &
     CONTRACTL_END;
 
     m_writeErrorEncountered = false;
-    m_pEntryObject = &object;
     m_currentPos = 0;
-    m_nextForwardReference = 0;
     m_pFileStream = new CFileStream();
     if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
     {
@@ -30,11 +32,7 @@ FastSerializer::FastSerializer(SString &outputFilePath, FastSerializableObject &
         return;
     }
 
-    // Write the file header.
     WriteFileHeader();
-
-    // Write the entry object.
-    WriteEntryObject();
 }
 
 FastSerializer::~FastSerializer()
@@ -47,15 +45,6 @@ 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);
@@ -74,23 +63,20 @@ void FastSerializer::WriteObject(FastSerializableObject *pObject)
 {
     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);
 }
 
@@ -138,82 +124,12 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
     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);
     }
@@ -234,6 +150,7 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
     // Write the SerializationType TypeName field.
     const char *strTypeName = pObject->GetTypeName();
     unsigned int length = (unsigned int)strlen(strTypeName);
+
     WriteString(strTypeName, length);
 
     // Write the EndObject tag.
@@ -246,7 +163,7 @@ void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned in
     CONTRACTL
     {
         NOTHROW;
-        GC_TRIGGERS;
+        GC_NOTRIGGER;
         MODE_PREEMPTIVE;
     }
     CONTRACTL_END;
@@ -259,18 +176,17 @@ void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned in
     }
 }
 
-
 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);
 }
@@ -280,7 +196,7 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length)
     CONTRACTL
     {
         NOTHROW;
-        GC_TRIGGERS;
+        GC_NOTRIGGER;
         MODE_PREEMPTIVE;
     }
     CONTRACTL_END;
@@ -292,47 +208,4 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length)
     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*) &current, sizeof(current));
-}
-
 #endif // FEATURE_PERFTRACING
index 5fd2cfd..fad04de 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef __FASTSERIALIZER_H__
 #define __FASTSERIALIZER_H__
 
+#define ALIGNMENT_SIZE 4
+
 #ifdef FEATURE_PERFTRACING
 
 #include "fastserializableobject.h"
@@ -14,30 +16,33 @@ class FastSerializer;
 
 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;
@@ -47,26 +52,21 @@ public:
     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
index 1bee70e..871b175 100644 (file)
@@ -35,6 +35,13 @@ class SampleProfiler
         // 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.
index af0c619..a85271a 100644 (file)
@@ -68,7 +68,7 @@ namespace Tracing.Tests
                         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");