From: David Mason Date: Wed, 20 May 2020 18:17:06 +0000 (-0700) Subject: Add managed array type support for EventPipe (#36242) X-Git-Tag: submit/tizen/20210909.063632~7859 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b4fc2f26178a251b833ba810f5ce8156ae2dc508;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Add managed array type support for EventPipe (#36242) Add support for emitting an event with an arbitrary number of arguments over EventPipe. --- diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.CoreCLR.cs index bd1ad77..7d3ab6c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.CoreCLR.cs @@ -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, void* pMetadata, 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); diff --git a/src/coreclr/src/vm/eventpipeblock.cpp b/src/coreclr/src/vm/eventpipeblock.cpp index d727159..dd8807d 100644 --- a/src/coreclr/src/vm/eventpipeblock.cpp +++ b/src/coreclr/src/vm/eventpipeblock.cpp @@ -462,6 +462,4 @@ bool EventPipeStackBlock::WriteStack(DWORD stackId, StackContents* pStack) return true; } - - #endif // FEATURE_PERFTRACING diff --git a/src/coreclr/src/vm/eventpipeevent.cpp b/src/coreclr/src/vm/eventpipeevent.cpp index e352afb..a56ed94 100644 --- a/src/coreclr/src/vm/eventpipeevent.cpp +++ b/src/coreclr/src/vm/eventpipeevent.cpp @@ -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 { diff --git a/src/coreclr/src/vm/eventpipeevent.h b/src/coreclr/src/vm/eventpipeevent.h index f2dfe4e..06da70f 100644 --- a/src/coreclr/src/vm/eventpipeevent.h +++ b/src/coreclr/src/vm/eventpipeevent.h @@ -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(); diff --git a/src/coreclr/src/vm/eventpipefile.cpp b/src/coreclr/src/vm/eventpipefile.cpp index 6baeaee..5a69f50 100644 --- a/src/coreclr/src/vm/eventpipefile.cpp +++ b/src/coreclr/src/vm/eventpipefile.cpp @@ -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)) { diff --git a/src/coreclr/src/vm/eventpipeprovider.cpp b/src/coreclr/src/vm/eventpipeprovider.cpp index f2d7760..1995b06 100644 --- a/src/coreclr/src/vm/eventpipeprovider.cpp +++ b/src/coreclr/src/vm/eventpipeprovider.cpp @@ -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; diff --git a/src/coreclr/src/vm/eventpipeprovider.h b/src/coreclr/src/vm/eventpipeprovider.h index d842f18..2fe93e9 100644 --- a/src/coreclr/src/vm/eventpipeprovider.h +++ b/src/coreclr/src/vm/eventpipeprovider.h @@ -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: diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs index 34a7b86..8d0afa1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs @@ -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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs index fd81d05..50f1f41 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs @@ -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 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(); - 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(); + 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()); + } + } + } + + 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()); + 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()); + } } } - 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(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 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 diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs index fd0bd6e..dbc70ab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs @@ -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; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index fd42fb6..90e260a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -691,7 +691,7 @@ namespace System.Diagnostics.Tracing uint eventVersion = m_eventData[i].Descriptor.Version; uint level = m_eventData[i].Descriptor.Level; - fixed (byte* pMetadata = 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); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IEventProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IEventProvider.cs index 2ceb92f..fc50ec7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IEventProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IEventProvider.cs @@ -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); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs index 92a1a82..56d0e06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs @@ -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, diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs index 17bf2eb..4ca0b83 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs @@ -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(