Add managed array type support for EventPipe (#36242)
authorDavid Mason <davmason@microsoft.com>
Wed, 20 May 2020 18:17:06 +0000 (11:17 -0700)
committerGitHub <noreply@github.com>
Wed, 20 May 2020 18:17:06 +0000 (11:17 -0700)
Add support for emitting an event with an arbitrary number of arguments over EventPipe.

14 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.CoreCLR.cs
src/coreclr/src/vm/eventpipeblock.cpp
src/coreclr/src/vm/eventpipeevent.cpp
src/coreclr/src/vm/eventpipeevent.h
src/coreclr/src/vm/eventpipefile.cpp
src/coreclr/src/vm/eventpipeprovider.cpp
src/coreclr/src/vm/eventpipeprovider.h
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IEventProvider.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs

index bd1ad77..7d3ab6c 100644 (file)
@@ -33,7 +33,7 @@ namespace System.Diagnostics.Tracing
         internal static extern IntPtr CreateProvider(string providerName, Interop.Advapi32.EtwEnableCallback callbackFunc);
 
         [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
-        internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, long keywords, uint eventVersion, uint level, voidpMetadata, uint metadataLength);
+        internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, long keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength);
 
         [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
         internal static extern IntPtr GetProvider(string providerName);
index d727159..dd8807d 100644 (file)
@@ -462,6 +462,4 @@ bool EventPipeStackBlock::WriteStack(DWORD stackId, StackContents* pStack)
 
     return true;
 }
-
-
 #endif // FEATURE_PERFTRACING
index e352afb..a56ed94 100644 (file)
@@ -23,7 +23,8 @@ EventPipeEvent::EventPipeEvent(
                                    m_level(level),
                                    m_needStack(needStack),
                                    m_enabledMask(0),
-                                   m_pMetadata(nullptr)
+                                   m_pMetadata(nullptr),
+                                   m_metadataLength(0)
 {
     CONTRACTL
     {
index f2dfe4e..06da70f 100644 (file)
@@ -49,7 +49,8 @@ private:
 
     // Only EventPipeProvider can create events.
     // The provider is responsible for allocating and freeing events.
-    EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
+    EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack,
+        BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
 
 public:
     ~EventPipeEvent();
index 6baeaee..5a69f50 100644 (file)
@@ -201,10 +201,11 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance, ULONGLONG captu
         THROWS;
         GC_NOTRIGGER;
         MODE_ANY;
-        PRECONDITION(m_pSerializer != nullptr);
     }
     CONTRACTL_END;
 
+    if (HasErrors()) return;
+
 #ifdef DEBUG
     _ASSERTE(instance.GetTimeStamp()->QuadPart >= m_lastSortedTimestamp.QuadPart);
     if (isSortedEvent)
@@ -221,14 +222,15 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance, ULONGLONG captu
 
     // 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());
+    EventPipeEvent* pEvent = instance.GetEvent();
+    unsigned int metadataId = GetMetadataId(*pEvent);
     if(metadataId == 0)
     {
         metadataId = GenerateMetadataId();
 
         EventPipeEventInstance* pMetadataInstance = EventPipe::BuildEventMetadataEvent(instance, metadataId);
 
-        WriteEventToBlock(*pMetadataInstance, 0); // metadataId=0 breaks recursion and represents the metadata event.
+        WriteEventToBlock(*pMetadataInstance, 0);  // metadataId=0 breaks recursion and represents the metadata event.
 
         SaveMetadataId(*instance.GetEvent(), metadataId);
 
@@ -247,7 +249,6 @@ void EventPipeFile::WriteSequencePoint(EventPipeSequencePoint* pSequencePoint)
         GC_NOTRIGGER;
         MODE_ANY;
         PRECONDITION(pSequencePoint != nullptr);
-        PRECONDITION(m_pSerializer != nullptr);
     }
     CONTRACTL_END;
 
@@ -259,6 +260,9 @@ void EventPipeFile::WriteSequencePoint(EventPipeSequencePoint* pSequencePoint)
 
     Flush(FlushAllBlocks);
     EventPipeSequencePointBlock sequencePointBlock(pSequencePoint);
+
+    if (HasErrors()) return;
+
     m_pSerializer->WriteObject(&sequencePointBlock);
 
     // stack cache resets on sequence points
@@ -278,13 +282,14 @@ void EventPipeFile::Flush(FlushFlags flags)
         NOTHROW;
         GC_NOTRIGGER;
         MODE_ANY;
-        PRECONDITION(m_pSerializer != nullptr);
         PRECONDITION(m_pMetadataBlock != nullptr);
         PRECONDITION(m_pStackBlock != nullptr);
         PRECONDITION(m_pBlock != nullptr);
     }
     CONTRACTL_END;
 
+    if (HasErrors()) return;
+
     // we write current blocks to the disk, whether they are full or not
     if ((m_pMetadataBlock->GetBytesWritten() != 0) && ((flags & FlushMetadataBlock) != 0))
     {
index f2d7760..1995b06 100644 (file)
@@ -152,7 +152,8 @@ EventPipeProviderCallbackData EventPipeProvider::UnsetConfiguration(
     return PrepareCallbackData(m_keywords, m_providerLevel, pFilterData);
 }
 
-EventPipeEvent *EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, BYTE *pMetadata, unsigned int metadataLength)
+EventPipeEvent *EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack,
+    BYTE *pMetadata, unsigned int metadataLength)
 {
     CONTRACTL
     {
@@ -172,7 +173,6 @@ EventPipeEvent *EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords
         needStack,
         pMetadata,
         metadataLength);
-
     // Add it to the list of events.
     AddEvent(*pEvent);
     return pEvent;
index d842f18..2fe93e9 100644 (file)
@@ -76,7 +76,8 @@ public:
     INT64 ComputeEventEnabledMask(INT64 keywords, EventPipeEventLevel eventLevel) const;
 
     // Create a new event.
-    EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
+    EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack,
+        BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
 
 private:
 
index 34a7b86..8d0afa1 100644 (file)
@@ -80,7 +80,8 @@ namespace System.Diagnostics.Tracing
         }
 
         // Define an EventPipeEvent handle.
-        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength)
+        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level,
+            byte *pMetadata, uint metadataLength)
         {
             IntPtr eventHandlePtr = EventPipeInternal.DefineEvent(m_provHandle, eventID, keywords, eventVersion, level, pMetadata, metadataLength);
             return eventHandlePtr;
index fd81d05..50f1f41 100644 (file)
@@ -9,6 +9,12 @@ namespace System.Diagnostics.Tracing
 #if FEATURE_PERFTRACING
     internal sealed class EventPipeMetadataGenerator
     {
+        private enum MetadataTag
+        {
+            Opcode = 1,
+            ParameterPayload = 2
+        }
+
         public static EventPipeMetadataGenerator Instance = new EventPipeMetadataGenerator();
 
         private EventPipeMetadataGenerator() { }
@@ -19,7 +25,8 @@ namespace System.Diagnostics.Tracing
             EventParameterInfo[] eventParams = new EventParameterInfo[parameters.Length];
             for (int i = 0; i < parameters.Length; i++)
             {
-                eventParams[i].SetInfo(parameters[i].Name!, parameters[i].ParameterType);
+                EventParameterInfo.GetTypeInfoFromType(parameters[i].ParameterType, out TraceLoggingTypeInfo? paramTypeInfo);
+                eventParams[i].SetInfo(parameters[i].Name!, parameters[i].ParameterType, paramTypeInfo);
             }
 
             return GenerateMetadata(
@@ -28,6 +35,7 @@ namespace System.Diagnostics.Tracing
                 eventMetadata.Descriptor.Keywords,
                 eventMetadata.Descriptor.Level,
                 eventMetadata.Descriptor.Version,
+                (EventOpcode)eventMetadata.Descriptor.Opcode,
                 eventParams);
         }
 
@@ -37,6 +45,7 @@ namespace System.Diagnostics.Tracing
             EventKeywords keywords,
             EventLevel level,
             uint version,
+            EventOpcode opcode,
             TraceLoggingEventTypes eventTypes)
         {
             TraceLoggingTypeInfo[] typeInfos = eventTypes.typeInfos;
@@ -52,7 +61,7 @@ namespace System.Diagnostics.Tracing
                 eventParams[i].SetInfo(paramName, typeInfos[i].DataType, typeInfos[i]);
             }
 
-            return GenerateMetadata(eventId, eventName, (long)keywords, (uint)level, version, eventParams);
+            return GenerateMetadata(eventId, eventName, (long)keywords, (uint)level, version, opcode, eventParams);
         }
 
         internal unsafe byte[]? GenerateMetadata(
@@ -61,9 +70,11 @@ namespace System.Diagnostics.Tracing
             long keywords,
             uint level,
             uint version,
+            EventOpcode opcode,
             EventParameterInfo[] parameters)
         {
             byte[]? metadata = null;
+            bool hasV2ParameterTypes = false;
             try
             {
                 // eventID          : 4 bytes
@@ -72,8 +83,9 @@ namespace System.Diagnostics.Tracing
                 // eventVersion     : 4 bytes
                 // level            : 4 bytes
                 // parameterCount   : 4 bytes
-                uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2;
-                uint defaultMetadataLength = metadataLength;
+                uint v1MetadataLength = 24 + ((uint)eventName.Length + 1) * 2;
+                uint v2MetadataLength = 0;
+                uint defaultV1MetadataLength = v1MetadataLength;
 
                 // Check for an empty payload.
                 // Write<T> calls with no arguments by convention have a parameter of
@@ -86,42 +98,115 @@ namespace System.Diagnostics.Tracing
                 // Increase the metadataLength for parameters.
                 foreach (EventParameterInfo parameter in parameters)
                 {
-                    int pMetadataLength = parameter.GetMetadataLength();
-                    // The call above may return -1 which means we failed to get the metadata length.
-                    // We then return a default metadata blob (with parameterCount of 0) to prevent it from generating malformed metadata.
-                    if (pMetadataLength < 0)
+                    uint pMetadataLength;
+                    if (!parameter.GetMetadataLength(out pMetadataLength))
                     {
-                        parameters = Array.Empty<EventParameterInfo>();
-                        metadataLength = defaultMetadataLength;
+                        // The call above may return false which means it is an unsupported type for V1.
+                        // If that is the case we use the v2 blob for metadata instead
+                        hasV2ParameterTypes = true;
                         break;
                     }
-                    metadataLength += (uint)pMetadataLength;
+
+                    v1MetadataLength += (uint)pMetadataLength;
+                }
+
+
+                if (hasV2ParameterTypes)
+                {
+                    v1MetadataLength = defaultV1MetadataLength;
+
+                    // V2 length is the parameter count (4 bytes) plus the size of the params
+                    v2MetadataLength = 4;
+                    foreach (EventParameterInfo parameter in parameters)
+                    {
+                        uint pMetadataLength;
+                        if (!parameter.GetMetadataLengthV2(out pMetadataLength))
+                        {
+                            // We ran in to an unsupported type, return empty event metadata
+                            parameters = Array.Empty<EventParameterInfo>();
+                            v1MetadataLength = defaultV1MetadataLength;
+                            v2MetadataLength = 0;
+                            hasV2ParameterTypes = false;
+                            break;
+                        }
+
+                        v2MetadataLength += (uint)pMetadataLength;
+                    }
                 }
 
-                metadata = new byte[metadataLength];
+                // Optional opcode length needs 1 byte for the opcode + 5 bytes for the tag (4 bytes size, 1 byte kind)
+                uint opcodeMetadataLength = opcode == EventOpcode.Info ? 0u : 6u;
+                // Optional V2 metadata needs the size of the params + 5 bytes for the tag (4 bytes size, 1 byte kind)
+                uint v2MetadataPayloadLength = v2MetadataLength == 0 ? 0 : v2MetadataLength + 5;
+                uint totalV2MetadataLength = v2MetadataPayloadLength + opcodeMetadataLength;
+                uint totalMetadataLength = v1MetadataLength + totalV2MetadataLength;
+                metadata = new byte[totalMetadataLength];
 
-                // Write metadata: eventID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name...
+                // Write metadata: metadataHeaderLength, eventID, eventName, keywords, eventVersion, level,
+                //                 parameterCount, param1..., optional extended metadata
                 fixed (byte* pMetadata = metadata)
                 {
                     uint offset = 0;
-                    WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)eventId);
+
+                    WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (uint)eventId);
                     fixed (char* pEventName = eventName)
                     {
-                        WriteToBuffer(pMetadata, metadataLength, ref offset, (byte*)pEventName, ((uint)eventName.Length + 1) * 2);
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (byte*)pEventName, ((uint)eventName.Length + 1) * 2);
                     }
-                    WriteToBuffer(pMetadata, metadataLength, ref offset, keywords);
-                    WriteToBuffer(pMetadata, metadataLength, ref offset, version);
-                    WriteToBuffer(pMetadata, metadataLength, ref offset, level);
-                    WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)parameters.Length);
-                    foreach (EventParameterInfo parameter in parameters)
+                    WriteToBuffer(pMetadata, totalMetadataLength, ref offset, keywords);
+                    WriteToBuffer(pMetadata, totalMetadataLength, ref offset, version);
+                    WriteToBuffer(pMetadata, totalMetadataLength, ref offset, level);
+
+                    if (hasV2ParameterTypes)
+                    {
+                        // If we have unsupported types, the V1 metadata must be empty. Write 0 count of params.
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, 0);
+                    }
+                    else
+                    {
+                        // Without unsupported V1 types we can write all the params now.
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (uint)parameters.Length);
+                        foreach (var parameter in parameters)
+                        {
+                            if (!parameter.GenerateMetadata(pMetadata, ref offset, totalMetadataLength))
+                            {
+                                // If we fail to generate metadata for any parameter, we should return the "default" metadata without any parameters
+                                return GenerateMetadata(eventId, eventName, keywords, level, version, opcode, Array.Empty<EventParameterInfo>());
+                            }
+                        }
+                    }
+
+                    Debug.Assert(offset == v1MetadataLength);
+
+                    if (opcode != EventOpcode.Info)
                     {
-                        if (!parameter.GenerateMetadata(pMetadata, ref offset, metadataLength))
+                        // Size of opcode
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, 1);
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (byte)MetadataTag.Opcode);
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (byte)opcode);
+                    }
+
+                    if (hasV2ParameterTypes)
+                    {
+                        // Write the V2 supported metadata now
+                        // Starting with the size of the V2 payload
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, v2MetadataLength);
+                        // Now the tag to identify it as a V2 parameter payload
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (byte)MetadataTag.ParameterPayload);
+                        // Then the count of parameters
+                        WriteToBuffer(pMetadata, totalMetadataLength, ref offset, (uint)parameters.Length);
+                        // Finally the parameters themselves
+                        foreach (var parameter in parameters)
                         {
-                            // If we fail to generate metadata for any parameter, we should return the "default" metadata without any parameters
-                            return GenerateMetadata(eventId, eventName, keywords, level, version, Array.Empty<EventParameterInfo>());
+                            if (!parameter.GenerateMetadataV2(pMetadata, ref offset, totalMetadataLength))
+                            {
+                                // If we fail to generate metadata for any parameter, we should return the "default" metadata without any parameters
+                                return GenerateMetadata(eventId, eventName, keywords, level, version, opcode, Array.Empty<EventParameterInfo>());
+                            }
                         }
                     }
-                    Debug.Assert(metadataLength == offset);
+
+                    Debug.Assert(totalMetadataLength == offset);
                 }
             }
             catch
@@ -147,28 +232,11 @@ namespace System.Diagnostics.Tracing
             offset += srcLength;
         }
 
-        // Copy uint value to buffer.
-        internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, uint value)
-        {
-            Debug.Assert(bufferLength >= (offset + 4));
-            *(uint*)(buffer + offset) = value;
-            offset += 4;
-        }
-
-        // Copy long value to buffer.
-        internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, long value)
+        internal static unsafe void WriteToBuffer<T>(byte* buffer, uint bufferLength, ref uint offset, T value) where T : unmanaged
         {
-            Debug.Assert(bufferLength >= (offset + 8));
-            *(long*)(buffer + offset) = value;
-            offset += 8;
-        }
-
-        // Copy char value to buffer.
-        internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, char value)
-        {
-            Debug.Assert(bufferLength >= (offset + 2));
-            *(char*)(buffer + offset) = value;
-            offset += 2;
+            Debug.Assert(bufferLength >= (offset + sizeof(T)));
+            *(T*)(buffer + offset) = value;
+            offset += (uint)sizeof(T);
         }
     }
 
@@ -307,16 +375,207 @@ namespace System.Diagnostics.Tracing
             return true;
         }
 
-        internal int GetMetadataLength()
+        internal unsafe bool GenerateMetadataV2(byte* pMetadataBlob, ref uint offset, uint blobSize)
+        {
+            if (TypeInfo == null)
+                return false;
+            return GenerateMetadataForNamedTypeV2(ParameterName, TypeInfo, pMetadataBlob, ref offset, blobSize);
+        }
+
+        private static unsafe bool GenerateMetadataForNamedTypeV2(string name, TraceLoggingTypeInfo typeInfo, byte* pMetadataBlob, ref uint offset, uint blobSize)
         {
-            int ret = 0;
+            Debug.Assert(pMetadataBlob != null);
+
+            if (!GetMetadataLengthForNamedTypeV2(name, typeInfo, out uint length))
+            {
+                return false;
+            }
+
+            EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, length);
+
+            // Write the property name.
+            fixed (char *pPropertyName = name)
+            {
+                EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte *)pPropertyName, ((uint)name.Length + 1) * 2);
+            }
+
+            return GenerateMetadataForTypeV2(typeInfo, pMetadataBlob, ref offset, blobSize);
+        }
+
+        private static unsafe bool GenerateMetadataForTypeV2(TraceLoggingTypeInfo? typeInfo, byte* pMetadataBlob, ref uint offset, uint blobSize)
+        {
+            Debug.Assert(typeInfo != null);
+            Debug.Assert(pMetadataBlob != null);
+
+            // Check if this type is a nested struct.
+            if (typeInfo is InvokeTypeInfo invokeTypeInfo)
+            {
+                // Each nested struct is serialized as:
+                //     TypeCode.Object              : 4 bytes
+                //     Number of properties         : 4 bytes
+                //     Property description 0...N
+                EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)TypeCode.Object);
+
+                // Get the set of properties to be serialized.
+                PropertyAnalysis[]? properties = invokeTypeInfo.properties;
+                if (properties != null)
+                {
+                    // Write the count of serializable properties.
+                    EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)properties.Length);
+
+                    foreach (PropertyAnalysis prop in properties)
+                    {
+                        if (!GenerateMetadataForNamedTypeV2(prop.name, prop.typeInfo, pMetadataBlob, ref offset, blobSize))
+                        {
+                            return false;
+                        }
+                    }
+                }
+                else
+                {
+                    // This struct has zero serializable properties so we just write the property count.
+                    EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)0);
+                }
+            }
+            else if (typeInfo is EnumerableTypeInfo enumerableTypeInfo)
+            {
+                // Each enumerable is serialized as:
+                //     TypeCode.Array               : 4 bytes
+                //     ElementType                  : N bytes
+                EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, EventPipeTypeCodeArray);
+                GenerateMetadataForTypeV2(enumerableTypeInfo.ElementInfo, pMetadataBlob, ref offset, blobSize);
+            }
+            else if (typeInfo is ScalarArrayTypeInfo arrayTypeInfo)
+            {
+                // Each enumerable is serialized as:
+                //     TypeCode.Array               : 4 bytes
+                //     ElementType                  : N bytes
+                if (!arrayTypeInfo.DataType.HasElementType)
+                {
+                    return false;
+                }
+
+                TraceLoggingTypeInfo? elementTypeInfo;
+                if (!GetTypeInfoFromType(arrayTypeInfo.DataType.GetElementType(), out elementTypeInfo))
+                {
+                    return false;
+                }
+
+                EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, EventPipeTypeCodeArray);
+                GenerateMetadataForTypeV2(elementTypeInfo, pMetadataBlob, ref offset, blobSize);
+            }
+            else
+            {
+                // Each primitive type is serialized as:
+                //     TypeCode : 4 bytes
+                TypeCode typeCode = GetTypeCodeExtended(typeInfo.DataType);
+
+                // EventPipe does not support this type.  Throw, which will cause no metadata to be registered for this event.
+                if (typeCode == TypeCode.Object)
+                {
+                    return false;
+                }
+
+                // Write the type code.
+                EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)typeCode);
+            }
+            return true;
+        }
+
+        internal static bool GetTypeInfoFromType(Type? type, out TraceLoggingTypeInfo? typeInfo)
+        {
+            if (type == typeof(bool))
+            {
+                typeInfo = ScalarTypeInfo.Boolean();
+                return true;
+            }
+            else if (type == typeof(byte))
+            {
+                typeInfo = ScalarTypeInfo.Byte();
+                return true;
+            }
+            else if (type == typeof(sbyte))
+            {
+                typeInfo = ScalarTypeInfo.SByte();
+                return true;
+            }
+            else if (type == typeof(char))
+            {
+                typeInfo = ScalarTypeInfo.Char();
+                return true;
+            }
+            else if (type == typeof(short))
+            {
+                typeInfo = ScalarTypeInfo.Int16();
+                return true;
+            }
+            else if (type == typeof(ushort))
+            {
+                typeInfo = ScalarTypeInfo.UInt16();
+                return true;
+            }
+            else if (type == typeof(int))
+            {
+                typeInfo = ScalarTypeInfo.Int32();
+                return true;
+            }
+            else if (type == typeof(uint))
+            {
+                typeInfo = ScalarTypeInfo.UInt32();
+                return true;
+            }
+            else if (type == typeof(long))
+            {
+                typeInfo = ScalarTypeInfo.Int64();
+                return true;
+            }
+            else if (type == typeof(ulong))
+            {
+                typeInfo = ScalarTypeInfo.UInt64();
+                return true;
+            }
+            else if (type == typeof(IntPtr))
+            {
+                typeInfo = ScalarTypeInfo.IntPtr();
+                return true;
+            }
+            else if (type == typeof(UIntPtr))
+            {
+                typeInfo = ScalarTypeInfo.UIntPtr();
+                return true;
+            }
+            else if (type == typeof(float))
+            {
+                typeInfo = ScalarTypeInfo.Single();
+                return true;
+            }
+            else if (type == typeof(double))
+            {
+                typeInfo = ScalarTypeInfo.Double();
+                return true;
+            }
+            else if (type == typeof(Guid))
+            {
+                typeInfo = ScalarTypeInfo.Guid();
+                return true;
+            }
+            else
+            {
+                typeInfo = null;
+                return false;
+            }
+        }
+
+        internal bool GetMetadataLength(out uint size)
+        {
+            size = 0;
 
             TypeCode typeCode = GetTypeCodeExtended(ParameterType);
             if (typeCode == TypeCode.Object)
             {
                 if (!(TypeInfo is InvokeTypeInfo typeInfo))
                 {
-                    return -1;
+                    return false;
                 }
 
                 // Each nested struct is serialized as:
@@ -324,7 +583,7 @@ namespace System.Diagnostics.Tracing
                 //     Number of properties : 4 bytes
                 //     Property description 0...N
                 //     Nested struct property name  : NULL-terminated string.
-                ret += sizeof(uint)  // TypeCode
+                size += sizeof(uint)  // TypeCode
                      + sizeof(uint); // Property count
 
                 // Get the set of properties to be serialized.
@@ -333,21 +592,21 @@ namespace System.Diagnostics.Tracing
                 {
                     foreach (PropertyAnalysis prop in properties)
                     {
-                        ret += (int)GetMetadataLengthForProperty(prop);
+                        size += GetMetadataLengthForProperty(prop);
                     }
                 }
 
                 // For simplicity when writing a reader, we write a NULL char
                 // after the metadata for a top-level struct (for its name) so that
                 // readers don't have do special case the outer-most struct.
-                ret += sizeof(char);
+                size += sizeof(char);
             }
             else
             {
-                ret += (int)(sizeof(uint) + ((ParameterName.Length + 1) * 2));
+                size += (uint)(sizeof(uint) + ((ParameterName.Length + 1) * 2));
             }
 
-            return ret;
+            return true;
         }
 
         private static uint GetMetadataLengthForProperty(PropertyAnalysis property)
@@ -388,6 +647,9 @@ namespace System.Diagnostics.Tracing
             return ret;
         }
 
+        // Array is not part of TypeCode, we decided to use 19 to represent it. (18 is the last type code value, string)
+        private const int EventPipeTypeCodeArray = 19;
+
         private static TypeCode GetTypeCodeExtended(Type parameterType)
         {
             // Guid is not part of TypeCode, we decided to use 17 to represent it, as it's the "free slot"
@@ -406,6 +668,99 @@ namespace System.Diagnostics.Tracing
 
             return Type.GetTypeCode(parameterType);
         }
+
+        internal bool GetMetadataLengthV2(out uint size)
+        {
+            return GetMetadataLengthForNamedTypeV2(ParameterName, TypeInfo, out size);
+        }
+
+        private static bool GetMetadataLengthForTypeV2(TraceLoggingTypeInfo? typeInfo, out uint size)
+        {
+            size = 0;
+            if (typeInfo == null)
+            {
+                return false;
+            }
+
+            if (typeInfo is InvokeTypeInfo invokeTypeInfo)
+            {
+                // Struct is serialized as:
+                //     TypeCode.Object      : 4 bytes
+                //     Number of properties : 4 bytes
+                //     Property description 0...N
+                size += sizeof(uint)  // TypeCode
+                     + sizeof(uint); // Property count
+
+                // Get the set of properties to be serialized.
+                PropertyAnalysis[]? properties = invokeTypeInfo.properties;
+                if (properties != null)
+                {
+                    foreach (PropertyAnalysis prop in properties)
+                    {
+                        if (!GetMetadataLengthForNamedTypeV2(prop.name, prop.typeInfo, out uint typeSize))
+                        {
+                            return false;
+                        }
+
+                        size += typeSize;
+                    }
+                }
+            }
+            else if (typeInfo is EnumerableTypeInfo enumerableTypeInfo)
+            {
+                // IEnumerable<T> is serialized as:
+                //     TypeCode            : 4 bytes
+                //     ElementType         : N bytes
+                size += sizeof(uint);
+                if (!GetMetadataLengthForTypeV2(enumerableTypeInfo.ElementInfo, out uint typeSize))
+                {
+                    return false;
+                }
+
+                size += typeSize;
+            }
+            else if (typeInfo is ScalarArrayTypeInfo arrayTypeInfo)
+            {
+                TraceLoggingTypeInfo? elementTypeInfo;
+                if (!arrayTypeInfo.DataType.HasElementType
+                    || !GetTypeInfoFromType(arrayTypeInfo.DataType.GetElementType(), out elementTypeInfo))
+                {
+                    return false;
+                }
+
+                size += sizeof(uint);
+                if (!GetMetadataLengthForTypeV2(elementTypeInfo, out uint typeSize))
+                {
+                    return false;
+                }
+
+                size += typeSize;
+            }
+            else
+            {
+                size += (uint)sizeof(uint);
+            }
+
+            return true;
+        }
+
+        private static bool GetMetadataLengthForNamedTypeV2(string name, TraceLoggingTypeInfo? typeInfo, out uint size)
+        {
+            // Named type is serialized
+            //     SizeOfTypeDescription    : 4 bytes
+            //     Name                     : NULL-terminated UTF16 string
+            //     Type                     : N bytes
+            size = (uint)(sizeof(uint) +
+                   ((name.Length + 1) * 2));
+
+            if (!GetMetadataLengthForTypeV2(typeInfo, out uint typeSize))
+            {
+                return false;
+            }
+
+            size += typeSize;
+            return true;
+        }
     }
 
 #endif // FEATURE_PERFTRACING
index fd0bd6e..dbc70ab 100644 (file)
@@ -1325,7 +1325,8 @@ namespace System.Diagnostics.Tracing
         }
 
         // Define an EventPipeEvent handle.
-        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength)
+        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion,
+            uint level, byte* pMetadata, uint metadataLength)
         {
             throw new System.NotSupportedException();
         }
@@ -1366,7 +1367,8 @@ namespace System.Diagnostics.Tracing
         }
 
         // Define an EventPipeEvent handle.
-        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength)
+        unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion,
+            uint level, byte* pMetadata, uint metadataLength)
         {
             return IntPtr.Zero;
         }
index fd42fb6..90e260a 100644 (file)
@@ -691,7 +691,7 @@ namespace System.Diagnostics.Tracing
                 uint eventVersion = m_eventData[i].Descriptor.Version;
                 uint level = m_eventData[i].Descriptor.Level;
 
-                fixed (bytepMetadata = metadata)
+                fixed (byte *pMetadata = metadata)
                 {
                     IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(
                         eventID,
@@ -2221,12 +2221,13 @@ namespace System.Diagnostics.Tracing
                                     string eventName = "EventSourceMessage";
                                     EventParameterInfo paramInfo = default(EventParameterInfo);
                                     paramInfo.SetInfo("message", typeof(string));
-                                    byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateMetadata(0, eventName, keywords, (uint)level, 0, new EventParameterInfo[] { paramInfo });
+                                    byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateMetadata(0, eventName, keywords, (uint)level, 0, EventOpcode.Info, new EventParameterInfo[] { paramInfo });
                                     uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
 
                                     fixed (byte* pMetadata = metadata)
                                     {
-                                        m_writeEventStringEventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(0, eventName, keywords, 0, (uint)level, pMetadata, metadataLength);
+                                        m_writeEventStringEventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(0, eventName, keywords, 0, (uint)level,
+                                                                            pMetadata, metadataLength);
                                     }
                                 }
                             }
index 2ceb92f..fc50ec7 100644 (file)
@@ -39,6 +39,7 @@ namespace System.Diagnostics.Tracing
         int EventActivityIdControl(Interop.Advapi32.ActivityControl ControlCode, ref Guid ActivityId);
 
         // Define an EventPipeEvent handle.
-        unsafe IntPtr DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength);
+        unsafe IntPtr DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion,
+            uint level, byte *pMetadata, uint metadataLength);
     }
 }
index 92a1a82..56d0e06 100644 (file)
@@ -25,6 +25,8 @@ namespace System.Diagnostics.Tracing
             this.elementInfo = elementInfo;
         }
 
+        internal TraceLoggingTypeInfo ElementInfo { get { return elementInfo; } }
+
         public override void WriteMetadata(
             TraceLoggingMetadataCollector collector,
             string? name,
index 17bf2eb..4ca0b83 100644 (file)
@@ -88,18 +88,19 @@ namespace System.Diagnostics.Tracing
                 {
                     if ((eventHandle = eventHandleTable[descriptor.EventId]) == IntPtr.Zero)
                     {
-                        byte[]? metadataBlob = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(
+                        byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(
                             descriptor.EventId,
                             name,
                             (EventKeywords)descriptor.Keywords,
                             (EventLevel)descriptor.Level,
                             descriptor.Version,
+                            (EventOpcode)descriptor.Opcode,
                             eventTypes);
-                        uint metadataLength = (metadataBlob != null) ? (uint)metadataBlob.Length : 0;
+                        uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
 
                         unsafe
                         {
-                            fixed (byte* pMetadataBlob = metadataBlob)
+                            fixed (byte* pMetadataBlob = metadata)
                             {
                                 // Define the event.
                                 eventHandle = provider.m_eventProvider.DefineEventHandle(