<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\FrameworkEventSource.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipe.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeMetadataGenerator.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Contracts\Contracts.cs" />
}
}
+ internal int TraceLoggingId
+ {
+ get
+ {
+ return m_traceloggingId;
+ }
+ }
+
public override bool Equals(object obj)
{
if (!(obj is EventDescriptor))
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
internal unsafe bool WriteEventRaw(
ref EventDescriptor eventDescriptor,
+ IntPtr eventHandle,
Guid* activityID,
Guid* relatedActivityID,
int dataCount,
status = m_eventProvider.EventWriteTransferWrapper(
m_regHandle,
ref eventDescriptor,
- IntPtr.Zero,
+ eventHandle,
activityID,
relatedActivityID,
dataCount,
}
}
- #region protected
+#region protected
/// <summary>
/// This is the constructor that most users will use to create their eventSource. It takes
/// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
// typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere.
private unsafe void DefineEventPipeEvents()
{
+ // If the EventSource is set to emit all events as TraceLogging events, skip this initialization.
+ // Events will be defined when they are emitted for the first time.
+ if(SelfDescribingEvents)
+ {
+ return;
+ }
+
Debug.Assert(m_eventData != null);
Debug.Assert(m_provider != null);
int cnt = m_eventData.Length;
if (eventID == 0)
continue;
+ byte[] metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(m_eventData[i]);
+
string eventName = m_eventData[i].Name;
Int64 keywords = m_eventData[i].Descriptor.Keywords;
uint eventVersion = m_eventData[i].Descriptor.Version;
uint level = m_eventData[i].Descriptor.Level;
- // evnetID : 4 bytes
- // eventName : (eventName.Length + 1) * 2 bytes
- // keywords : 8 bytes
- // eventVersion : 4 bytes
- // level : 4 bytes
- // parameterCount : 4 bytes
- uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2;
-
- // Increase the metadataLength for the types of all parameters.
- metadataLength += (uint)m_eventData[i].Parameters.Length * 4;
-
- // Increase the metadataLength for the names of all parameters.
- foreach (var parameter in m_eventData[i].Parameters)
- {
- string parameterName = parameter.Name;
- metadataLength = metadataLength + ((uint)parameterName.Length + 1) * 2;
- }
-
- byte[] metadata = new byte[metadataLength];
-
- // Write metadata: evnetID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name...
fixed (byte *pMetadata = metadata)
{
- uint offset = 0;
- WriteToBuffer(pMetadata, metadataLength, ref offset, eventID);
- fixed(char *pEventName = eventName)
- {
- WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pEventName, ((uint)eventName.Length + 1) * 2);
- }
- WriteToBuffer(pMetadata, metadataLength, ref offset, keywords);
- WriteToBuffer(pMetadata, metadataLength, ref offset, eventVersion);
- WriteToBuffer(pMetadata, metadataLength, ref offset, level);
- WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)m_eventData[i].Parameters.Length);
- foreach (var parameter in m_eventData[i].Parameters)
- {
- // Write parameter type.
- WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)GetTypeCodeExtended(parameter.ParameterType));
-
- // Write parameter name.
- string parameterName = parameter.Name;
- fixed (char *pParameterName = parameterName)
- {
- WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pParameterName, ((uint)parameterName.Length + 1) * 2);
- }
- }
- Debug.Assert(metadataLength == offset);
- IntPtr eventHandle = m_provider.m_eventProvider.DefineEventHandle(eventID, eventName, keywords, eventVersion, level, pMetadata, metadataLength);
- m_eventData[i].EventHandle = eventHandle;
+ IntPtr eventHandle = m_provider.m_eventProvider.DefineEventHandle(
+ eventID,
+ eventName,
+ keywords,
+ eventVersion,
+ level,
+ pMetadata,
+ (uint)metadata.Length);
+
+ Debug.Assert(eventHandle != IntPtr.Zero);
+ m_eventData[i].EventHandle = eventHandle;
}
}
}
-
- // Copy src to buffer and modify the offset.
- // Note: We know the buffer size ahead of time to make sure no buffer overflow.
- private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength)
- {
- Debug.Assert(bufferLength >= (offset + srcLength));
- for (int i = 0; i < srcLength; i++)
- {
- *(byte *)(buffer + offset + i) = *(byte *)(src + i);
- }
- offset += srcLength;
- }
-
- // Copy uint value to buffer.
- private 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.
- private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, long value)
- {
- Debug.Assert(bufferLength >= (offset + 8));
- *(long *)(buffer + offset) = value;
- offset += 8;
- }
-
- 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"
- // see https://github.com/dotnet/coreclr/issues/16105#issuecomment-361749750 for more
- const TypeCode GuidTypeCode = (TypeCode)17;
-
- if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
- return GuidTypeCode;
-
- return Type.GetTypeCode(parameterType);
- }
#endif
internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
/// </summary>
internal int Reserved { get { return m_Reserved; } set { m_Reserved = value; } }
- #region private
+#region private
/// <summary>
/// Initializes the members of this EventData object to point at a previously-pinned
/// tracelogging-compatible metadata blob.
#pragma warning disable 0649
internal int m_Reserved; // Used to pad the size to match the Win32 API
#pragma warning restore 0649
- #endregion
+#endregion
}
/// <summary>
WriteEventVarargs(eventId, &relatedActivityId, args);
}
- #endregion
+#endregion
- #region IDisposable Members
+#region IDisposable Members
/// <summary>
/// Disposes of an EventSource.
/// </summary>
{
this.Dispose(false);
}
- #endregion
+#endregion
- #region private
+#region private
private unsafe void WriteEventRaw(
string eventName,
ref EventDescriptor eventDescriptor,
+ IntPtr eventHandle,
Guid* activityID,
Guid* relatedActivityID,
int dataCount,
}
else
{
- if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
+ if (!m_provider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
ThrowEventSourceException(eventName);
}
#endif // FEATURE_MANAGED_ETW
Debug.Assert(!SelfDescribingEvents);
-#if FEATURE_MANAGED_ETW
+#if FEATURE_MANAGED_ETW
fixed (byte* dataPtr = rawManifest)
{
// we don't want the manifest to show up in the event log channels so we specify as keywords
/// </typeparam>
internal sealed class InvokeTypeInfo : TraceLoggingTypeInfo
{
- private readonly PropertyAnalysis[] properties;
+ internal readonly PropertyAnalysis[] properties;
public InvokeTypeInfo(
Type type,
internal readonly int identity;
internal readonly byte[] nameMetadata;
+#if FEATURE_PERFTRACING
+ private IntPtr eventHandle = IntPtr.Zero;
+ private readonly object eventHandleCreationLock = new object();
+#endif
+
public NameInfo(string name, EventTags tags, int typeMetadataSize)
{
this.name = name;
}
return result;
}
+
+#if FEATURE_PERFTRACING
+ public IntPtr GetOrCreateEventHandle(EventProvider provider, EventDescriptor descriptor, TraceLoggingEventTypes eventTypes)
+ {
+ if (eventHandle == IntPtr.Zero)
+ {
+ lock (eventHandleCreationLock)
+ {
+ if (eventHandle == IntPtr.Zero)
+ {
+ byte[] metadataBlob = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(
+ descriptor.EventId,
+ name,
+ (EventKeywords)descriptor.Keywords,
+ (EventLevel)descriptor.Level,
+ descriptor.Version,
+ eventTypes);
+
+ unsafe
+ {
+ fixed (byte* pMetadataBlob = metadataBlob)
+ {
+ // Define the event.
+ eventHandle = provider.m_eventProvider.DefineEventHandle(
+ (uint)descriptor.EventId,
+ name,
+ descriptor.Keywords,
+ descriptor.Version,
+ descriptor.Level,
+ pMetadataBlob,
+ (uint)metadataBlob.Length);
+ }
+ }
+ }
+ }
+ }
+
+ return eventHandle;
+ }
+#endif
}
}
identity = nameInfo.identity;
EventDescriptor descriptor = new EventDescriptor(identity, level, opcode, (long)keywords);
+#if FEATURE_PERFTRACING
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, descriptor, eventTypes);
+ Debug.Assert(eventHandle != IntPtr.Zero);
+#else
+ IntPtr eventHandle = IntPtr.Zero;
+#endif
+
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
this.WriteEventRaw(
eventName,
ref descriptor,
+ eventHandle,
activityID,
childActivityID,
(int)(DataCollector.ThreadInstance.Finish() - descriptors),
return;
}
+#if FEATURE_PERFTRACING
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, descriptor, eventTypes);
+ Debug.Assert(eventHandle != IntPtr.Zero);
+#else
+ IntPtr eventHandle = IntPtr.Zero;
+#endif
+
// We make a descriptor for each EventData, and because we morph strings to counted strings
// we may have 2 for each arg, so we allocate enough for this.
var descriptorsLength = eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3;
this.WriteEventRaw(
eventName,
ref descriptor,
+ eventHandle,
activityID,
childActivityID,
numDescrs,
return;
}
+#if FEATURE_PERFTRACING
+ IntPtr eventHandle = nameInfo.GetOrCreateEventHandle(m_provider, descriptor, eventTypes);
+ Debug.Assert(eventHandle != IntPtr.Zero);
+#else
+ IntPtr eventHandle = IntPtr.Zero;
+#endif
+
#if FEATURE_MANAGED_ETW
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
#endif // FEATURE_MANAGED_ETW
#if (!ES_BUILD_PCL && !ES_BUILD_PN)
- System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
#endif
EventOpcode opcode = (EventOpcode)descriptor.Opcode;
this.WriteEventRaw(
eventName,
ref descriptor,
+ eventHandle,
pActivityId,
pRelatedActivityId,
(int)(DataCollector.ThreadInstance.Finish() - descriptors),
public class TraceLoggingEventTypes
{
internal readonly TraceLoggingTypeInfo[] typeInfos;
+#if FEATURE_PERFTRACING
+ internal readonly string[] paramNames;
+#endif
internal readonly string name;
internal readonly EventTags tags;
internal readonly byte level;
}
this.typeInfos = MakeArray(paramInfos);
+#if FEATURE_PERFTRACING
+ this.paramNames = MakeParamNameArray(paramInfos);
+#endif
this.name = name;
this.tags = tags;
this.level = Statics.DefaultLevel;
return (TraceLoggingTypeInfo[])typeInfos.Clone(); ;
}
+
+#if FEATURE_PERFTRACING
+ private static string[] MakeParamNameArray(
+ System.Reflection.ParameterInfo[] paramInfos)
+ {
+ string[] paramNames = new string[paramInfos.Length];
+ for (int i = 0; i < paramNames.Length; i++)
+ {
+ paramNames[i] = paramInfos[i].Name;
+ }
+
+ return paramNames;
+ }
+#endif
}
}
return 0;
}
+ // If Channel == 11, this is a TraceLogging event.
+ // The first 3 descriptors contain event metadata that is emitted for ETW and should be discarded on EventPipe.
+ // EventPipe metadata is provided via the EventPipeEventProvider.DefineEventHandle.
+ if (eventDescriptor.Channel == 11)
+ {
+ userData = userData + 3;
+ userDataCount = userDataCount - 3;
+ Debug.Assert(userDataCount >= 0);
+ }
EventPipeInternal.WriteEventData(eventHandle, eventID, &userData, (uint) userDataCount, activityId, relatedActivityId);
}
return 0;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.Reflection;
+using EventMetadata = System.Diagnostics.Tracing.EventSource.EventMetadata;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+
+ internal sealed class EventPipeMetadataGenerator
+ {
+ public static EventPipeMetadataGenerator Instance = new EventPipeMetadataGenerator();
+
+ private EventPipeMetadataGenerator() { }
+
+ public unsafe byte[] GenerateEventMetadata(EventMetadata eventMetadata)
+ {
+ ParameterInfo[] parameters = eventMetadata.Parameters;
+ EventParameterInfo[] eventParams = new EventParameterInfo[parameters.Length];
+ for(int i=0; i<parameters.Length; i++)
+ {
+ eventParams[i].SetInfo(parameters[i].Name, parameters[i].ParameterType);
+ }
+
+ return GenerateMetadata(
+ eventMetadata.Descriptor.EventId,
+ eventMetadata.Name,
+ eventMetadata.Descriptor.Keywords,
+ eventMetadata.Descriptor.Level,
+ eventMetadata.Descriptor.Version,
+ eventParams);
+ }
+
+ public unsafe byte[] GenerateEventMetadata(
+ int eventId,
+ string eventName,
+ EventKeywords keywords,
+ EventLevel level,
+ uint version,
+ TraceLoggingEventTypes eventTypes)
+ {
+ TraceLoggingTypeInfo[] typeInfos = eventTypes.typeInfos;
+ string[] paramNames = eventTypes.paramNames;
+ EventParameterInfo[] eventParams = new EventParameterInfo[typeInfos.Length];
+ for(int i=0; i<typeInfos.Length; i++)
+ {
+ string paramName = string.Empty;
+ if(paramNames != null)
+ {
+ paramName = paramNames[i];
+ }
+ eventParams[i].SetInfo(paramName, typeInfos[i].DataType, typeInfos[i]);
+ }
+
+ return GenerateMetadata(eventId, eventName, (long)keywords, (uint)level, version, eventParams);
+ }
+
+ private unsafe byte[] GenerateMetadata(
+ int eventId,
+ string eventName,
+ long keywords,
+ uint level,
+ uint version,
+ EventParameterInfo[] parameters)
+ {
+ // eventID : 4 bytes
+ // eventName : (eventName.Length + 1) * 2 bytes
+ // keywords : 8 bytes
+ // eventVersion : 4 bytes
+ // level : 4 bytes
+ // parameterCount : 4 bytes
+ uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2;
+
+ // Check for an empty payload.
+ // Write<T> calls with no arguments by convention have a parameter of
+ // type NullTypeInfo which is serialized as nothing.
+ if((parameters.Length == 1) && (parameters[0].ParameterType == typeof(EmptyStruct)))
+ {
+ parameters = Array.Empty<EventParameterInfo>();
+ }
+
+ // Increase the metadataLength for parameters.
+ foreach (var parameter in parameters)
+ {
+ metadataLength = metadataLength + parameter.GetMetadataLength();
+ }
+
+ byte[] metadata = new byte[metadataLength];
+
+ // Write metadata: eventID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name...
+ fixed (byte *pMetadata = metadata)
+ {
+ uint offset = 0;
+ WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)eventId);
+ fixed(char *pEventName = eventName)
+ {
+ WriteToBuffer(pMetadata, metadataLength, 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 (var parameter in parameters)
+ {
+ parameter.GenerateMetadata(pMetadata, ref offset, metadataLength);
+ }
+ Debug.Assert(metadataLength == offset);
+ }
+
+ return metadata;
+ }
+
+ // Copy src to buffer and modify the offset.
+ // Note: We know the buffer size ahead of time to make sure no buffer overflow.
+ internal static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength)
+ {
+ Debug.Assert(bufferLength >= (offset + srcLength));
+ for (int i = 0; i < srcLength; i++)
+ {
+ *(byte *)(buffer + offset + i) = *(byte *)(src + i);
+ }
+ 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)
+ {
+ 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;
+ }
+
+ }
+
+ internal struct EventParameterInfo
+ {
+ internal string ParameterName;
+ internal Type ParameterType;
+ internal TraceLoggingTypeInfo TypeInfo;
+
+ internal void SetInfo(string name, Type type, TraceLoggingTypeInfo typeInfo = null)
+ {
+ ParameterName = name;
+ ParameterType = type;
+ TypeInfo = typeInfo;
+ }
+
+ internal unsafe void GenerateMetadata(byte* pMetadataBlob, ref uint offset, uint blobSize)
+ {
+ TypeCode typeCode = GetTypeCodeExtended(ParameterType);
+ if(typeCode == TypeCode.Object)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)TypeCode.Object);
+
+ InvokeTypeInfo invokeTypeInfo = TypeInfo as InvokeTypeInfo;
+ if(invokeTypeInfo == null)
+ {
+ throw new NotSupportedException();
+ }
+
+ // 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)
+ {
+ GenerateMetadataForProperty(prop, pMetadataBlob, ref offset, blobSize);
+ }
+ }
+ else
+ {
+ // This struct has zero serializable properties so we just write the property count.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)0);
+ }
+
+ // Top-level structs don't have a property name, but for simplicity we write a NULL-char to represent the name.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, '\0');
+
+ }
+ else
+ {
+ // Write parameter type.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)typeCode);
+
+ // Write parameter name.
+ fixed (char *pParameterName = ParameterName)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte *)pParameterName, ((uint)ParameterName.Length + 1) * 2);
+ }
+ }
+ }
+
+ private static unsafe void GenerateMetadataForProperty(PropertyAnalysis property, byte* pMetadataBlob, ref uint offset, uint blobSize)
+ {
+ Debug.Assert(property != null);
+ Debug.Assert(pMetadataBlob != null);
+
+ // Check if this property is a nested struct.
+ InvokeTypeInfo invokeTypeInfo = property.typeInfo as InvokeTypeInfo;
+ if(invokeTypeInfo != null)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ 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)
+ {
+ GenerateMetadataForProperty(prop, pMetadataBlob, ref offset, blobSize);
+ }
+ }
+ else
+ {
+ // This struct has zero serializable properties so we just write the property count.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)0);
+ }
+
+ // Write the property name.
+ fixed(char *pPropertyName = property.name)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte *)pPropertyName, ((uint)property.name.Length + 1) * 2);
+ }
+ }
+ else
+ {
+ // Each primitive type is serialized as:
+ // TypeCode : 4 bytes
+ // PropertyName : NULL-terminated string
+ TypeCode typeCode = GetTypeCodeExtended(property.typeInfo.DataType);
+ Debug.Assert(typeCode != TypeCode.Object);
+
+ // Write the type code.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)typeCode);
+
+ // Write the property name.
+ fixed(char *pPropertyName = property.name)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte *)pPropertyName, ((uint)property.name.Length + 1) * 2);
+ }
+ }
+ }
+
+
+ internal unsafe uint GetMetadataLength()
+ {
+ uint ret = 0;
+
+ TypeCode typeCode = GetTypeCodeExtended(ParameterType);
+ if(typeCode == TypeCode.Object)
+ {
+ InvokeTypeInfo typeInfo = TypeInfo as InvokeTypeInfo;
+ if(typeInfo == null)
+ {
+ throw new NotSupportedException();
+ }
+
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ ret += sizeof(uint) // TypeCode
+ + sizeof(uint); // Property count
+
+ // Get the set of properties to be serialized.
+ PropertyAnalysis[] properties = typeInfo.properties;
+ if(properties != null)
+ {
+ foreach(PropertyAnalysis prop in properties)
+ {
+ ret += 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);
+ }
+ else
+ {
+ ret += (uint)(sizeof(uint) + ((ParameterName.Length + 1) * 2));
+ }
+
+ return ret;
+ }
+
+ private static uint GetMetadataLengthForProperty(PropertyAnalysis property)
+ {
+ Debug.Assert(property != null);
+
+ uint ret = 0;
+
+ // Check if this property is a nested struct.
+ InvokeTypeInfo invokeTypeInfo = property.typeInfo as InvokeTypeInfo;
+ if(invokeTypeInfo != null)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ ret += 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)
+ {
+ ret += GetMetadataLengthForProperty(prop);
+ }
+ }
+
+ // Add the size of the property name.
+ ret += (uint)((property.name.Length + 1) * 2);
+ }
+ else
+ {
+ ret += (uint)(sizeof(uint) + ((property.name.Length + 1) * 2));
+ }
+
+ return ret;
+ }
+
+ 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"
+ // see https://github.com/dotnet/coreclr/issues/16105#issuecomment-361749750 for more
+ const TypeCode GuidTypeCode = (TypeCode)17;
+
+ if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
+ return GuidTypeCode;
+
+ return Type.GetTypeCode(parameterType);
+ }
+ }
+
+#endif // FEATURE_PERFTRACING
+}
<ExcludeList Include="$(XunitTestBinBase)\tracing\eventsourcetrace\eventsourcetrace\eventsourcetrace.cmd">
<Issue>15494</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\tracevalidation\tracelogging\tracelogging\tracelogging.cmd">
+ <Issue>15494</Issue>
+ </ExcludeList>
</ItemGroup>
<!-- Failures while testing via ILLINK -->
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.Parsers;
+
+namespace Tracing.Tests.Common
+{
+ public sealed class EventSourceTestSuite
+ {
+ private NetPerfFile m_file;
+ private List<EventSource> m_eventSources = new List<EventSource>();
+ private List<EventSourceTest> m_tests = new List<EventSourceTest>();
+ private int m_nextTestVerificationIndex = 0;
+
+ public EventSourceTestSuite(
+ NetPerfFile file)
+ {
+ if(file == null)
+ {
+ throw new ArgumentNullException(nameof(file));
+ }
+
+ m_file = file;
+ }
+
+ public void AddEventSource(EventSource source)
+ {
+ if(source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ m_eventSources.Add(source);
+ }
+
+ public void AddTest(EventSourceTest test)
+ {
+ if(test == null)
+ {
+ throw new ArgumentNullException(nameof(test));
+ }
+ m_tests.Add(test);
+ }
+
+ public void RunTests()
+ {
+ // Get the configuration and start tracing.
+ TraceConfiguration traceConfig = GenerateConfiguration();
+ TraceControl.Enable(traceConfig);
+
+ // Run the tests.
+ foreach(EventSourceTest test in m_tests)
+ {
+ test.LogEvent();
+ }
+
+ // Stop tracing.
+ TraceControl.Disable();
+
+ // Open the trace file.
+ string traceLogPath = TraceLog.CreateFromEventPipeDataFile(m_file.Path);
+ using(TraceLog traceLog = new TraceLog(traceLogPath))
+ {
+ TraceEventDispatcher dispatcher = traceLog.Events.GetSource();
+
+ dispatcher.Dynamic.All += delegate(TraceEvent data)
+ {
+ if(data.ProviderName.EndsWith("Rundown"))
+ {
+ return;
+ }
+
+ Assert.True($"m_nextTestVerificationIndex({m_nextTestVerificationIndex}) < m_tests.Count({m_tests.Count})", m_nextTestVerificationIndex < m_tests.Count);
+ try
+ {
+ Console.WriteLine($"Verifying Event: {data.ToString()}");
+ m_tests[m_nextTestVerificationIndex].VerifyEvent(data);
+ }
+ catch
+ {
+ Console.WriteLine($"Failure during test '{m_tests[m_nextTestVerificationIndex].Name}'.");
+ throw;
+ }
+
+ m_nextTestVerificationIndex++;
+ };
+
+ dispatcher.Process();
+ Assert.Equal("Test Count", m_tests.Count, m_nextTestVerificationIndex);
+ }
+ }
+
+ private TraceConfiguration GenerateConfiguration()
+ {
+ uint circularBufferMB = 1024; // 1 GB
+
+ TraceConfiguration traceConfig = new TraceConfiguration(m_file.Path, circularBufferMB);
+
+ // Add each of the registered EventSources.
+ foreach(EventSource source in m_eventSources)
+ {
+ traceConfig.EnableProvider(
+ source.Name, // ProviderName
+ 0xFFFFFFFFFFFFFFFF, // Keywords
+ 5); // Level
+ }
+
+ return traceConfig;
+ }
+ }
+
+ public delegate void LogEventDelegate();
+ public delegate void VerifyEventDelegate(TraceEvent eventData);
+
+ public sealed class EventSourceTest
+ {
+ private string m_name;
+ private LogEventDelegate m_logEvent;
+ private VerifyEventDelegate m_verifyEvent;
+
+ public EventSourceTest(
+ string name,
+ LogEventDelegate logEvent,
+ VerifyEventDelegate verifyEvent)
+ {
+ if(String.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+ if(logEvent == null)
+ {
+ throw new ArgumentNullException(nameof(logEvent));
+ }
+ if(verifyEvent == null)
+ {
+ throw new ArgumentNullException(nameof(verifyEvent));
+ }
+
+ m_name = name;
+ m_logEvent = logEvent;
+ m_verifyEvent = verifyEvent;
+ }
+
+ public string Name
+ {
+ get { return m_name; }
+ }
+
+ public void LogEvent()
+ {
+ m_logEvent();
+ }
+
+ public void VerifyEvent(TraceEvent eventData)
+ {
+ m_verifyEvent(eventData);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Tracing.Tests.Common;
+using System.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.Parsers;
+
+namespace Tracing.Tests
+{
+ [EventSource(Name = "ManifestEventSource")]
+ public class ManifestEventSource : EventSource
+ {
+ public static ManifestEventSource Log = new ManifestEventSource();
+
+ private ManifestEventSource()
+ : base(true)
+ {
+ }
+
+ [Event(1)]
+ public void EmptyEvent()
+ {
+ WriteEvent(1);
+ }
+
+ [Event(2)]
+ public void IntStringEvent(int i, string s)
+ {
+ WriteEvent(2, i, s);
+ }
+ }
+
+ [EventSource(Name = "TraceLoggingEventSource")]
+ public class TraceLoggingEventSource : EventSource
+ {
+ public static TraceLoggingEventSource Log = new TraceLoggingEventSource();
+
+ private TraceLoggingEventSource()
+ : base(EventSourceSettings.EtwSelfDescribingEventFormat)
+ {
+ }
+
+ [Event(1)]
+ public void EmptyEvent()
+ {
+ WriteEvent(1);
+ }
+
+ [Event(2)]
+ public void IntStringEvent(int i, string s)
+ {
+ WriteEvent(2, i, s);
+ }
+ }
+
+ public static class TraceLogging
+ {
+ public static int Main(string[] args)
+ {
+ using (NetPerfFile file = NetPerfFile.Create(args))
+ {
+ EventSourceTestSuite suite = new EventSourceTestSuite(file);
+ suite.AddEventSource(ManifestEventSource.Log);
+ suite.AddEventSource(TraceLoggingEventSource.Log);
+
+ suite.AddTest(new EventSourceTest("ManifestEmptyEvent",
+ delegate()
+ {
+ ManifestEventSource.Log.EmptyEvent();
+ },
+ delegate(TraceEvent eventData)
+ {
+ Assert.Equal("ProviderName", ManifestEventSource.Log.Name, eventData.ProviderName);
+ Assert.Equal("EventName", "EmptyEvent", eventData.EventName);
+ Assert.Equal("PayloadCount", 0, eventData.PayloadNames.Length);
+ }));
+
+ suite.AddTest(new EventSourceTest("TraceLoggingEmptyEvent",
+ delegate()
+ {
+ TraceLoggingEventSource.Log.EmptyEvent();
+ },
+ delegate(TraceEvent eventData)
+ {
+ Assert.Equal("ProviderName", TraceLoggingEventSource.Log.Name, eventData.ProviderName);
+ Assert.Equal("EventName", "EmptyEvent", eventData.EventName);
+ Assert.Equal("PayloadCount", 0, eventData.PayloadNames.Length);
+ }));
+
+ suite.AddTest(new EventSourceTest("ManifestIntString",
+ delegate()
+ {
+ ManifestEventSource.Log.IntStringEvent(42, "Hello World!");
+ },
+ delegate(TraceEvent eventData)
+ {
+ Assert.Equal("ProviderName", ManifestEventSource.Log.Name, eventData.ProviderName);
+ Assert.Equal("EventName", "IntStringEvent", eventData.EventName);
+ Assert.Equal("PayloadCount", 2, eventData.PayloadNames.Length);
+ Assert.Equal("i", 42, (int)eventData.PayloadValue(0));
+ Assert.Equal("s", "Hello World!", (string)eventData.PayloadValue(1));
+ }));
+
+ suite.AddTest(new EventSourceTest("TraceLoggingIntString",
+ delegate()
+ {
+ TraceLoggingEventSource.Log.IntStringEvent(42, "Hello World!");
+ },
+ delegate(TraceEvent eventData)
+ {
+ Assert.Equal("ProviderName", TraceLoggingEventSource.Log.Name, eventData.ProviderName);
+ Assert.Equal("EventName", "IntStringEvent", eventData.EventName);
+ Assert.Equal("PayloadCount", 2, eventData.PayloadNames.Length);
+ Assert.Equal("i", 42, (int)eventData.PayloadValue(0));
+ Assert.Equal("s", "Hello World!", (string)eventData.PayloadValue(1));
+ }));
+
+ suite.RunTests();
+ }
+
+ return 100;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{8E3244CB-407F-4142-BAAB-E7A55901A5FA}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <CLRTestKind>BuildAndRun</CLRTestKind>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <CLRTestPriority>0</CLRTestPriority>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="EventSourceTest.cs" />
+ <Compile Include="TraceLogging.cs" />
+ <ProjectReference Include="../../common/common.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>