diff options
Diffstat (limited to 'src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging')
31 files changed, 6800 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs new file mode 100644 index 0000000000..1b7772246c --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + internal sealed class ArrayTypeInfo<ElementType> + : TraceLoggingTypeInfo<ElementType[]> + { + private readonly TraceLoggingTypeInfo<ElementType> elementInfo; + + public ArrayTypeInfo(TraceLoggingTypeInfo<ElementType> elementInfo) + { + this.elementInfo = elementInfo; + } + + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.BeginBufferedArray(); + this.elementInfo.WriteMetadata(collector, name, format); + collector.EndBufferedArray(); + } + + public override void WriteData( + TraceLoggingDataCollector collector, + ref ElementType[] value) + { + var bookmark = collector.BeginBufferedArray(); + + var count = 0; + if (value != null) + { + count = value.Length; + for (int i = 0; i < value.Length; i++) + { + this.elementInfo.WriteData(collector, ref value[i]); + } + } + + collector.EndBufferedArray(bookmark, count); + } + + public override object GetData(object value) + { + var array = (ElementType[])value; + var serializedArray = new object[array.Length]; + for (int i = 0; i < array.Length; i++) + { + serializedArray[i] = this.elementInfo.GetData(array[i]); + } + return serializedArray; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs new file mode 100644 index 0000000000..b07d671f5a --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using Interlocked = System.Threading.Interlocked; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: A very simple lock-free add-only dictionary. + /// Warning: this is a copy-by-value type. Copying performs a snapshot. + /// Accessing a readonly field always makes a copy of the field, so the + /// GetOrAdd method will not work as expected if called on a readonly field. + /// </summary> + /// <typeparam name="KeyType"> + /// The type of the key, used for TryGet. + /// </typeparam> + /// <typeparam name="ItemType"> + /// The type of the item, used for GetOrAdd. + /// </typeparam> + internal struct ConcurrentSet<KeyType, ItemType> + where ItemType : ConcurrentSetItem<KeyType, ItemType> + { + private ItemType[] items; + + public ItemType TryGet(KeyType key) + { + ItemType item; + var oldItems = this.items; + + if (oldItems != null) + { + var lo = 0; + var hi = oldItems.Length; + do + { + int i = (lo + hi) / 2; + item = oldItems[i]; + + int cmp = item.Compare(key); + if (cmp == 0) + { + goto Done; + } + else if (cmp < 0) + { + lo = i + 1; + } + else + { + hi = i; + } + } + while (lo != hi); + } + + item = null; + + Done: + + return item; + } + + public ItemType GetOrAdd(ItemType newItem) + { + ItemType item; + var oldItems = this.items; + ItemType[] newItems; + + Retry: + + if (oldItems == null) + { + newItems = new ItemType[] { newItem }; + } + else + { + var lo = 0; + var hi = oldItems.Length; + do + { + int i = (lo + hi) / 2; + item = oldItems[i]; + + int cmp = item.Compare(newItem); + if (cmp == 0) + { + goto Done; + } + else if (cmp < 0) + { + lo = i + 1; + } + else + { + hi = i; + } + } + while (lo != hi); + + int oldLength = oldItems.Length; + newItems = new ItemType[oldLength + 1]; + Array.Copy(oldItems, 0, newItems, 0, lo); + newItems[lo] = newItem; + Array.Copy(oldItems, lo, newItems, lo + 1, oldLength - lo); + } + + newItems = Interlocked.CompareExchange(ref this.items, newItems, oldItems); + if (oldItems != newItems) + { + oldItems = newItems; + goto Retry; + } + + item = newItem; + + Done: + + return item; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs new file mode 100644 index 0000000000..322f664303 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Abstract base class that must be inherited by items in a + /// ConcurrentSet. + /// </summary> + /// <typeparam name="KeyType">Type of the set's key.</typeparam> + /// <typeparam name="ItemType">Type of the derived class.</typeparam> + internal abstract class ConcurrentSetItem<KeyType, ItemType> + where ItemType : ConcurrentSetItem<KeyType, ItemType> + { + public abstract int Compare(ItemType other); + public abstract int Compare(KeyType key); + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs new file mode 100644 index 0000000000..7166297e8a --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs @@ -0,0 +1,309 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Runtime.InteropServices; +using System.Security; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: This is the implementation of the DataCollector + /// functionality. To enable safe access to the DataCollector from + /// untrusted code, there is one thread-local instance of this structure + /// per thread. The instance must be Enabled before any data is written to + /// it. The instance must be Finished before the data is passed to + /// EventWrite. The instance must be Disabled before the arrays referenced + /// by the pointers are freed or unpinned. + /// </summary> + [SecurityCritical] + internal unsafe struct DataCollector + { + [ThreadStatic] + internal static DataCollector ThreadInstance; + + private byte* scratchEnd; + private EventSource.EventData* datasEnd; + private GCHandle* pinsEnd; + private EventSource.EventData* datasStart; + private byte* scratch; + private EventSource.EventData* datas; + private GCHandle* pins; + private byte[] buffer; + private int bufferPos; + private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this. + private bool writingScalars; + + internal void Enable( + byte* scratch, + int scratchSize, + EventSource.EventData* datas, + int dataCount, + GCHandle* pins, + int pinCount) + { + this.datasStart = datas; + this.scratchEnd = scratch + scratchSize; + this.datasEnd = datas + dataCount; + this.pinsEnd = pins + pinCount; + this.scratch = scratch; + this.datas = datas; + this.pins = pins; + this.writingScalars = false; + } + + internal void Disable() + { + this = new DataCollector(); + } + + /// <summary> + /// Completes the list of scalars. Finish must be called before the data + /// descriptor array is passed to EventWrite. + /// </summary> + /// <returns> + /// A pointer to the next unused data descriptor, or datasEnd if they were + /// all used. (Descriptors may be unused if a string or array was null.) + /// </returns> + internal EventSource.EventData* Finish() + { + this.ScalarsEnd(); + return this.datas; + } + + internal void AddScalar(void* value, int size) + { + var pb = (byte*)value; + if (this.bufferNesting == 0) + { + var scratchOld = this.scratch; + var scratchNew = scratchOld + size; + if (this.scratchEnd < scratchNew) + { + throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_AddScalarOutOfRange")); + } + + this.ScalarsBegin(); + this.scratch = scratchNew; + + for (int i = 0; i != size; i++) + { + scratchOld[i] = pb[i]; + } + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + for (int i = 0; i != size; i++, oldPos++) + { + this.buffer[oldPos] = pb[i]; + } + } + } + + internal void AddBinary(string value, int size) + { + if (size > ushort.MaxValue) + { + size = ushort.MaxValue - 1; + } + + if (this.bufferNesting != 0) + { + this.EnsureBuffer(size + 2); + } + + this.AddScalar(&size, 2); + + if (size != 0) + { + if (this.bufferNesting == 0) + { + this.ScalarsEnd(); + this.PinArray(value, size); + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + fixed (void* p = value) + { + Marshal.Copy((IntPtr)p, this.buffer, oldPos, size); + } + } + } + } + + internal void AddBinary(Array value, int size) + { + this.AddArray(value, size, 1); + } + + internal void AddArray(Array value, int length, int itemSize) + { + if (length > ushort.MaxValue) + { + length = ushort.MaxValue; + } + + var size = length * itemSize; + if (this.bufferNesting != 0) + { + this.EnsureBuffer(size + 2); + } + + this.AddScalar(&length, 2); + + if (length != 0) + { + if (this.bufferNesting == 0) + { + this.ScalarsEnd(); + this.PinArray(value, size); + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + Buffer.BlockCopy(value, 0, this.buffer, oldPos, size); + } + } + } + + /// <summary> + /// Marks the start of a non-blittable array or enumerable. + /// </summary> + /// <returns>Bookmark to be passed to EndBufferedArray.</returns> + internal int BeginBufferedArray() + { + this.BeginBuffered(); + this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable) + return this.bufferPos; + } + + /// <summary> + /// Marks the end of a non-blittable array or enumerable. + /// </summary> + /// <param name="bookmark">The value returned by BeginBufferedArray.</param> + /// <param name="count">The number of items in the array.</param> + internal void EndBufferedArray(int bookmark, int count) + { + this.EnsureBuffer(); + this.buffer[bookmark - 2] = unchecked((byte)count); + this.buffer[bookmark - 1] = unchecked((byte)(count >> 8)); + this.EndBuffered(); + } + + /// <summary> + /// Marks the start of dynamically-buffered data. + /// </summary> + internal void BeginBuffered() + { + this.ScalarsEnd(); + this.bufferNesting += 1; + } + + /// <summary> + /// Marks the end of dynamically-buffered data. + /// </summary> + internal void EndBuffered() + { + this.bufferNesting -= 1; + + if (this.bufferNesting == 0) + { + this.EnsureBuffer(); + this.PinArray(this.buffer, this.bufferPos); + this.buffer = null; + this.bufferPos = 0; + } + } + + private void EnsureBuffer() + { + var required = this.bufferPos; + if (this.buffer == null || this.buffer.Length < required) + { + this.GrowBuffer(required); + } + } + + private void EnsureBuffer(int additionalSize) + { + var required = this.bufferPos + additionalSize; + if (this.buffer == null || this.buffer.Length < required) + { + this.GrowBuffer(required); + } + } + + private void GrowBuffer(int required) + { + var newSize = this.buffer == null ? 64 : this.buffer.Length; + + do + { + newSize *= 2; + } + while (newSize < required); + + Array.Resize(ref this.buffer, newSize); + } + + private void PinArray(object value, int size) + { + var pinsTemp = this.pins; + if (this.pinsEnd <= pinsTemp) + { + throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_PinArrayOutOfRange")); + } + + var datasTemp = this.datas; + if (this.datasEnd <= datasTemp) + { + throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + } + + this.pins = pinsTemp + 1; + this.datas = datasTemp + 1; + + *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned); + datasTemp->m_Ptr = (long)(ulong)(UIntPtr)(void*)pinsTemp->AddrOfPinnedObject(); + datasTemp->m_Size = size; + } + + private void ScalarsBegin() + { + if (!this.writingScalars) + { + var datasTemp = this.datas; + if (this.datasEnd <= datasTemp) + { + throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + } + + datasTemp->m_Ptr = (long)(ulong)(UIntPtr)this.scratch; + this.writingScalars = true; + } + } + + private void ScalarsEnd() + { + if (this.writingScalars) + { + var datasTemp = this.datas; + datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr)); + this.datas = datasTemp + 1; + this.writingScalars = false; + } + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs new file mode 100644 index 0000000000..829020ac23 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Empty struct indicating no payload data. + /// </summary> + internal struct EmptyStruct + { + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs new file mode 100644 index 0000000000..db6317ee51 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Reflection; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Provides support for casting enums to their underlying type + /// from within generic context. + /// </summary> + /// <typeparam name="UnderlyingType"> + /// The underlying type of the enum. + /// </typeparam> + internal static class EnumHelper<UnderlyingType> + { + private delegate UnderlyingType Transformer<ValueType>(ValueType value); + + private static readonly MethodInfo IdentityInfo = + Statics.GetDeclaredStaticMethod(typeof(EnumHelper<UnderlyingType>), "Identity"); + + public static UnderlyingType Cast<ValueType>(ValueType value) + { + return Caster<ValueType>.Instance(value); + } + + internal static UnderlyingType Identity(UnderlyingType value) + { + return value; + } + + private static class Caster<ValueType> + { + public static readonly Transformer<ValueType> Instance = + (Transformer<ValueType>)Statics.CreateDelegate( + typeof(Transformer<ValueType>), + IdentityInfo); + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs new file mode 100644 index 0000000000..5ff6c07889 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + internal sealed class EnumerableTypeInfo<IterableType, ElementType> + : TraceLoggingTypeInfo<IterableType> + where IterableType : IEnumerable<ElementType> + { + private readonly TraceLoggingTypeInfo<ElementType> elementInfo; + + public EnumerableTypeInfo(TraceLoggingTypeInfo<ElementType> elementInfo) + { + this.elementInfo = elementInfo; + } + + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.BeginBufferedArray(); + this.elementInfo.WriteMetadata(collector, name, format); + collector.EndBufferedArray(); + } + + public override void WriteData( + TraceLoggingDataCollector collector, + ref IterableType value) + { + var bookmark = collector.BeginBufferedArray(); + + var count = 0; + if (value != null) + { + foreach (var element in value) + { + var el = element; + this.elementInfo.WriteData(collector, ref el); + count++; + } + } + + collector.EndBufferedArray(bookmark, count); + } + + public override object GetData(object value) + { + var iterType = (IterableType)value; + List<object> serializedEnumerable = new List<object>(); + foreach (var element in iterType) + { + serializedEnumerable.Add(elementInfo.GetData(element)); + } + return serializedEnumerable.ToArray(); + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs new file mode 100644 index 0000000000..905bf5ac1c --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Used when authoring types that will be passed to EventSource.Write. + /// EventSource.Write<T> only works when T is either an anonymous type + /// or a type with an [EventData] attribute. In addition, the properties + /// of T must be supported property types. Supported property types include + /// simple built-in types (int, string, Guid, DateTime, DateTimeOffset, + /// KeyValuePair, etc.), anonymous types that only contain supported types, + /// types with an [EventData] attribute, arrays of the above, and IEnumerable + /// of the above. + /// </summary> + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public class EventDataAttribute + : Attribute + { + private EventLevel level = (EventLevel)(-1); + private EventOpcode opcode = (EventOpcode)(-1); + + /// <summary> + /// Gets or sets the name to use if this type is used for an + /// implicitly-named event or an implicitly-named property. + /// + /// Example 1: + /// + /// EventSource.Write(null, new T()); // implicitly-named event + /// + /// The name of the event will be determined as follows: + /// + /// if (T has an EventData attribute and attribute.Name != null) + /// eventName = attribute.Name; + /// else + /// eventName = typeof(T).Name; + /// + /// Example 2: + /// + /// EventSource.Write(name, new { _1 = new T() }); // implicitly-named field + /// + /// The name of the field will be determined as follows: + /// + /// if (T has an EventData attribute and attribute.Name != null) + /// fieldName = attribute.Name; + /// else + /// fieldName = typeof(T).Name; + /// </summary> + public string Name + { + get; + set; + } + + /// <summary> + /// Gets or sets the level to use for the event. + /// Invalid levels (outside the range 0..255) are treated as unset. + /// Note that the Level attribute can bubble-up, i.e. if a type contains + /// a sub-object (a field or property), and the sub-object's type has a + /// TraceLoggingEvent attribute, the Level from the sub-object's attribute + /// can affect the event's level. + /// + /// Example: for EventSource.Write(name, options, data), the level of the + /// event will be determined as follows: + /// + /// if (options.Level has been set) + /// eventLevel = options.Level; + /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Level has been set) + /// eventLevel = attribute.Level; + /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Level has been set) + /// eventLevel = attribute.Level; + /// else + /// eventLevel = EventLevel.LogAlways; + /// </summary> + internal EventLevel Level + { + get { return this.level; } + set { this.level = value; } + } + + /// <summary> + /// Gets or sets the opcode to use for the event. + /// Invalid opcodes (outside the range 0..255) are treated as unset. + /// Note that the Opcode attribute can bubble-up, i.e. if a type contains + /// a sub-object (a field or property), and the sub-object's type has a + /// TraceLoggingEvent attribute, the Opcode from the sub-object's attribute + /// can affect the event's opcode. + /// + /// Example: for EventSource.Write(name, options, data), the opcode of the + /// event will be determined as follows: + /// + /// if (options.Opcode has been set) + /// eventOpcode = options.Opcode; + /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Opcode has been set) + /// eventOpcode = attribute.Opcode; + /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Opcode has been set) + /// eventOpcode = attribute.Opcode; + /// else + /// eventOpcode = EventOpcode.Info; + /// </summary> + internal EventOpcode Opcode + { + get { return this.opcode; } + set { this.opcode = value; } + } + + /// <summary> + /// Gets or sets the keywords to use for the event. + /// Note that the Keywords attribute can bubble-up, i.e. if a type contains + /// a sub-object (a field or property), and the sub-object's type has a + /// TraceLoggingEvent attribute, the Keywords from the sub-object's attribute + /// can affect the event's keywords. + /// + /// Example: for EventSource.Write(name, options, data), the keywords of the + /// event will be determined as follows: + /// + /// eventKeywords = options.Keywords; + /// if (data.GetType() has a TraceLoggingEvent attribute) + /// eventKeywords |= attribute.Keywords; + /// if (a field/property contained in data has a TraceLoggingEvent attribute) + /// eventKeywords |= attribute.Keywords; + /// </summary> + internal EventKeywords Keywords + { + get; + set; + } + + /// <summary> + /// Gets or sets the flags for an event. These flags are ignored by ETW, + /// but can have meaning to the event consumer. + /// </summary> + internal EventTags Tags + { + get; + set; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs new file mode 100644 index 0000000000..0b896b7fcf --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Tags are flags that are not interpreted by EventSource but are passed along + /// to the EventListener. The EventListener determines the semantics of the flags. + /// </summary> + [Flags] + public enum EventFieldTags + { + /// <summary> + /// No special traits are added to the field. + /// </summary> + None = 0, + + /* Bits below 0x10000 are available for any use by the provider. */ + /* Bits at or above 0x10000 are reserved for definition by Microsoft. */ + } + + /// <summary> + /// TraceLogging: used when authoring types that will be passed to EventSource.Write. + /// Controls how a field or property is handled when it is written as a + /// field in a TraceLogging event. Apply this attribute to a field or + /// property if the default handling is not correct. (Apply the + /// TraceLoggingIgnore attribute if the property should not be + /// included as a field in the event.) + /// The default for Name is null, which means that the name of the + /// underlying field or property will be used as the event field's name. + /// The default for PiiTag is 0, which means that the event field does not + /// contain personally-identifiable information. + /// </summary> + [AttributeUsage(AttributeTargets.Property)] + public class EventFieldAttribute + : Attribute + { + /// <summary> + /// User defined options for the field. These are not interpreted by the EventSource + /// but are available to the Listener. See EventFieldSettings for details + /// </summary> + public EventFieldTags Tags + { + get; + set; + } + + /// <summary> + /// Gets or sets the name to use for the field. This defaults to null. + /// If null, the name of the corresponding property will be used + /// as the event field's name. + /// </summary> + internal string Name + { + get; + set; + } + + /// <summary> + /// Gets or sets a field formatting hint. + /// </summary> + public EventFieldFormat Format + { + get; + set; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs new file mode 100644 index 0000000000..0505d6af63 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Provides a hint that may be used by an event listener when formatting + /// an event field for display. Note that the event listener may ignore the + /// hint if it does not recognize a particular combination of type and format. + /// Similar to TDH_OUTTYPE. + /// </summary> + public enum EventFieldFormat + { + /// <summary> + /// Field receives default formatting based on the field's underlying type. + /// </summary> + Default = 0, +#if false + /// <summary> + /// Field should not be displayed. + /// </summary> + NoPrint = 1, +#endif + /// <summary> + /// Field should be formatted as character or string data. + /// Typically applied to 8-bit or 16-bit integers. + /// This is the default format for String and Char types. + /// </summary> + String = 2, + + /// <summary> + /// Field should be formatted as boolean data. Typically applied to 8-bit + /// or 32-bit integers. This is the default format for the Boolean type. + /// </summary> + Boolean = 3, + + /// <summary> + /// Field should be formatted as hexadecimal data. Typically applied to + /// integer types. + /// </summary> + Hexadecimal = 4, + +#if false + /// <summary> + /// Field should be formatted as a process identifier. Typically applied to + /// 32-bit integer types. + /// </summary> + ProcessId = 5, + + /// <summary> + /// Field should be formatted as a thread identifier. Typically applied to + /// 32-bit integer types. + /// </summary> + ThreadId = 6, + + /// <summary> + /// Field should be formatted as an Internet port. Typically applied to 16-bit integer + /// types. + /// </summary> + Port = 7, + /// <summary> + /// Field should be formatted as an Internet Protocol v4 address. Typically applied to + /// 32-bit integer types. + /// </summary> + Ipv4Address = 8, + + /// <summary> + /// Field should be formatted as an Internet Protocol v6 address. Typically applied to + /// byte[] types. + /// </summary> + Ipv6Address = 9, + /// <summary> + /// Field should be formatted as a SOCKADDR. Typically applied to byte[] types. + /// </summary> + SocketAddress = 10, +#endif + /// <summary> + /// Field should be formatted as XML string data. Typically applied to + /// strings or arrays of 8-bit or 16-bit integers. + /// </summary> + Xml = 11, + + /// <summary> + /// Field should be formatted as JSON string data. Typically applied to + /// strings or arrays of 8-bit or 16-bit integers. + /// </summary> + Json = 12, +#if false + /// <summary> + /// Field should be formatted as a Win32 error code. Typically applied to + /// 32-bit integer types. + /// </summary> + Win32Error = 13, + + /// <summary> + /// Field should be formatted as an NTSTATUS code. Typically applied to + /// 32-bit integer types. + /// </summary> + NTStatus = 14, +#endif + /// <summary> + /// Field should be formatted as an HRESULT code. Typically applied to + /// 32-bit integer types. + /// </summary> + HResult = 15, +#if false + /// <summary> + /// Field should be formatted as a FILETIME. Typically applied to 64-bit + /// integer types. This is the default format for DateTime types. + /// </summary> + FileTime = 16, + /// <summary> + /// When applied to a numeric type, indicates that the type should be formatted + /// as a signed integer. This is the default format for signed integer types. + /// </summary> + Signed = 17, + + /// <summary> + /// When applied to a numeric type, indicates that the type should be formatted + /// as an unsigned integer. This is the default format for unsigned integer types. + /// </summary> + Unsigned = 18, +#endif + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs new file mode 100644 index 0000000000..367693f0cd --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Used when authoring types that will be passed to EventSource.Write. + /// By default, EventSource.Write will write all of an object's public + /// properties to the event payload. Apply [EventIgnore] to a public + /// property to prevent EventSource.Write from including the property in + /// the event. + /// </summary> + [AttributeUsage(AttributeTargets.Property)] + public class EventIgnoreAttribute + : Attribute + { + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs new file mode 100644 index 0000000000..3240a8d738 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs @@ -0,0 +1,147 @@ +using System.Collections.Generic; +using System.Collections; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// EventPayload class holds the list of parameters and their corresponding values for user defined types passed to + /// EventSource APIs. + /// Preserving the order of the elements as they were found inside user defined types is the most important characteristic of this class. + /// </summary> + internal class EventPayload : IDictionary<string, object> + { + internal EventPayload(List<string> payloadNames, List<object> payloadValues) + { + Contract.Assert(payloadNames.Count == payloadValues.Count); + + m_names = payloadNames; + m_values = payloadValues; + } + + public ICollection<string> Keys { get { return m_names; } } + public ICollection<object> Values { get { return m_values; } } + + public object this[string key] + { + get + { + if (key == null) + throw new System.ArgumentNullException("key"); + + int position = 0; + foreach(var name in m_names) + { + if (name == key) + { + return m_values[position]; + } + position++; + } + + throw new System.Collections.Generic.KeyNotFoundException(); + } + set + { + throw new System.NotSupportedException(); + } + } + + public void Add(string key, object value) + { + throw new System.NotSupportedException(); + } + + public void Add(KeyValuePair<string, object> payloadEntry) + { + throw new System.NotSupportedException(); + } + + public void Clear() + { + throw new System.NotSupportedException(); + } + + public bool Contains(KeyValuePair<string, object> entry) + { + return ContainsKey(entry.Key); + } + + public bool ContainsKey(string key) + { + if (key == null) + throw new System.ArgumentNullException("key"); + + foreach (var item in m_names) + { + if (item == key) + return true; + } + return false; + } + + public int Count { get { return m_names.Count; } } + + public bool IsReadOnly { get { return true; } } + + public IEnumerator<KeyValuePair<string, object>> GetEnumerator() + { + throw new System.NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + var instance = this as IEnumerable<KeyValuePair<string, object>>; + return instance.GetEnumerator(); + } + + public void CopyTo(KeyValuePair<string, object>[] payloadEntries, int count) + { + throw new System.NotSupportedException(); + } + + public bool Remove(string key) + { + throw new System.NotSupportedException(); + } + + public bool Remove(KeyValuePair<string, object> entry) + { + throw new System.NotSupportedException(); + } + + public bool TryGetValue(string key, out object value) + { + if (key == null) + throw new System.ArgumentNullException("key"); + + int position = 0; + foreach (var name in m_names) + { + if (name == key) + { + value = m_values[position]; + return true; + } + position++; + } + + value = default(object); + return false; + } + + #region private + private List<string> m_names; + private List<object> m_values; + #endregion + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceActivity.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceActivity.cs new file mode 100644 index 0000000000..2114707d69 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceActivity.cs @@ -0,0 +1,319 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Provides support for EventSource activities by marking the start and + /// end of a particular operation. + /// </summary> + internal sealed class EventSourceActivity + : IDisposable + { + /// <summary> + /// Initializes a new instance of the EventSourceActivity class that + /// is attached to the specified event source. The new activity will + /// not be attached to any related (parent) activity. + /// The activity is created in the Initialized state. + /// </summary> + /// <param name="eventSource"> + /// The event source to which the activity information is written. + /// </param> + public EventSourceActivity(EventSource eventSource) + { + if (eventSource == null) + throw new ArgumentNullException("eventSource"); + Contract.EndContractBlock(); + + this.eventSource = eventSource; + } + + /// <summary> + /// You can make an activity out of just an EventSource. + /// </summary> + public static implicit operator EventSourceActivity(EventSource eventSource) { return new EventSourceActivity(eventSource); } + + /* Properties */ + /// <summary> + /// Gets the event source to which this activity writes events. + /// </summary> + public EventSource EventSource + { + get { return this.eventSource; } + } + + /// <summary> + /// Gets this activity's unique identifier, or the default Guid if the + /// event source was disabled when the activity was initialized. + /// </summary> + public Guid Id + { + get { return this.activityId; } + } + +#if false // don't expose RelatedActivityId unless there is a need. + /// <summary> + /// Gets the unique identifier of this activity's related (parent) + /// activity. + /// </summary> + public Guid RelatedId + { + get { return this.relatedActivityId; } + } +#endif + + /// <summary> + /// Writes a Start event with the specified name and data. If the start event is not active (because the provider + /// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner + /// and it is effectively like the Start d + /// + /// A new activityID GUID is generated and the returned + /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes) + /// with this activityID. In addition the Start activity will log a 'relatedActivityID' that was the activity + /// ID before the start event. This way event processors can form a linked list of all the activities that + /// caused this one (directly or indirectly). + /// </summary> + /// <param name="eventName"> + /// The name to use for the event. It is strongly suggested that this name end in 'Start' (e.g. DownloadStart). + /// If you do this, then the Stop() method will automatically replace the 'Start' suffix with a 'Stop' suffix. + /// </param> + /// <param name="options">Allow options (keywords, level) to be set for the write associated with this start + /// These will also be used for the stop event.</param> + /// <param name="data">The data to include in the event.</param> + public EventSourceActivity Start<T>(string eventName, EventSourceOptions options, T data) + { + return this.Start(eventName, ref options, ref data); + } + /// <summary> + /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords + /// and level==Info) Data payload is empty. + /// </summary> + public EventSourceActivity Start(string eventName) + { + var options = new EventSourceOptions(); + var data = new EmptyStruct(); + return this.Start(eventName, ref options, ref data); + } + /// <summary> + /// Shortcut version see Start(string eventName, EventSourceOptions options, T data). Data payload is empty. + /// </summary> + public EventSourceActivity Start(string eventName, EventSourceOptions options) + { + var data = new EmptyStruct(); + return this.Start(eventName, ref options, ref data); + } + /// <summary> + /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords + /// and level==Info) + /// </summary> + public EventSourceActivity Start<T>(string eventName, T data) + { + var options = new EventSourceOptions(); + return this.Start(eventName, ref options, ref data); + } + + /// <summary> + /// Writes a Stop event with the specified data, and sets the activity + /// to the Stopped state. The name is determined by the eventName used in Start. + /// If that Start event name is suffixed with 'Start' that is removed, and regardless + /// 'Stop' is appended to the result to form the Stop event name. + /// May only be called when the activity is in the Started state. + /// </summary> + /// <param name="data">The data to include in the event.</param> + public void Stop<T>(T data) + { + this.Stop(null, ref data); + } + /// <summary> + /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') + /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. + /// </summary> + public void Stop<T>(string eventName) + { + var data = new EmptyStruct(); + this.Stop(eventName, ref data); + } + /// <summary> + /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') + /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. + /// </summary> + public void Stop<T>(string eventName, T data) + { + this.Stop(eventName, ref data); + } + + /// <summary> + /// Writes an event associated with this activity to the eventSource associted with this activity. + /// May only be called when the activity is in the Started state. + /// </summary> + /// <param name="eventName"> + /// The name to use for the event. If null, the name is determined from + /// data's type. + /// </param> + /// <param name="options"> + /// The options to use for the event. + /// </param> + /// <param name="data">The data to include in the event.</param> + public void Write<T>(string eventName, EventSourceOptions options, T data) + { + this.Write(this.eventSource, eventName, ref options, ref data); + } + /// <summary> + /// Writes an event associated with this activity. + /// May only be called when the activity is in the Started state. + /// </summary> + /// <param name="eventName"> + /// The name to use for the event. If null, the name is determined from + /// data's type. + /// </param> + /// <param name="data">The data to include in the event.</param> + public void Write<T>(string eventName, T data) + { + var options = new EventSourceOptions(); + this.Write(this.eventSource, eventName, ref options, ref data); + } + /// <summary> + /// Writes a trivial event associated with this activity. + /// May only be called when the activity is in the Started state. + /// </summary> + /// <param name="eventName"> + /// The name to use for the event. Must not be null. + /// </param> + /// <param name="options"> + /// The options to use for the event. + /// </param> + public void Write(string eventName, EventSourceOptions options) + { + var data = new EmptyStruct(); + this.Write(this.eventSource, eventName, ref options, ref data); + } + /// <summary> + /// Writes a trivial event associated with this activity. + /// May only be called when the activity is in the Started state. + /// </summary> + /// <param name="eventName"> + /// The name to use for the event. Must not be null. + /// </param> + public void Write(string eventName) + { + var options = new EventSourceOptions(); + var data = new EmptyStruct(); + this.Write(this.eventSource, eventName, ref options, ref data); + } + /// <summary> + /// Writes an event to a arbitrary eventSource stamped with the activity ID of this activity. + /// </summary> + public void Write<T>(EventSource source, string eventName, EventSourceOptions options, T data) + { + this.Write(source, eventName, ref options, ref data); + } + + /// <summary> + /// Releases any unmanaged resources associated with this object. + /// If the activity is in the Started state, calls Stop(). + /// </summary> + public void Dispose() + { + if (this.state == State.Started) + { + var data = new EmptyStruct(); + this.Stop(null, ref data); + } + } + + #region private + private EventSourceActivity Start<T>(string eventName, ref EventSourceOptions options, ref T data) + { + if (this.state != State.Started) + throw new InvalidOperationException(); + + // If the source is not on at all, then we don't need to do anything and we can simply return ourselves. + if (!this.eventSource.IsEnabled()) + return this; + + var newActivity = new EventSourceActivity(eventSource); + if (!this.eventSource.IsEnabled(options.Level, options.Keywords)) + { + // newActivity.relatedActivityId = this.Id; + Guid relatedActivityId = this.Id; + newActivity.activityId = Guid.NewGuid(); + newActivity.startStopOptions = options; + newActivity.eventName = eventName; + newActivity.startStopOptions.Opcode = EventOpcode.Start; + this.eventSource.Write(eventName, ref newActivity.startStopOptions, ref newActivity.activityId, ref relatedActivityId, ref data); + } + else + { + // If we are not active, we don't set the eventName, which basically also turns off the Stop event as well. + newActivity.activityId = this.Id; + } + + return newActivity; + } + + private void Write<T>(EventSource eventSource, string eventName, ref EventSourceOptions options, ref T data) + { + if (this.state != State.Started) + throw new InvalidOperationException(); // Write after stop. + if (eventName == null) + throw new ArgumentNullException(); + + eventSource.Write(eventName, ref options, ref this.activityId, ref s_empty, ref data); + } + + private void Stop<T>(string eventName, ref T data) + { + if (this.state != State.Started) + throw new InvalidOperationException(); + + // If start was not fired, then stop isn't as well. + if (!StartEventWasFired) + return; + + this.state = State.Stopped; + if (eventName == null) + { + eventName = this.eventName; + if (eventName.EndsWith("Start")) + eventName = eventName.Substring(0, eventName.Length - 5); + eventName = eventName + "Stop"; + } + this.startStopOptions.Opcode = EventOpcode.Stop; + this.eventSource.Write(eventName, ref this.startStopOptions, ref this.activityId, ref s_empty, ref data); + } + + private enum State + { + Started, + Stopped + } + + /// <summary> + /// If eventName is non-null then we logged a start event + /// </summary> + private bool StartEventWasFired { get { return eventName != null; }} + + private readonly EventSource eventSource; + private EventSourceOptions startStopOptions; + internal Guid activityId; + // internal Guid relatedActivityId; + private State state; + private string eventName; + + static internal Guid s_empty; + #endregion + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceOptions.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceOptions.cs new file mode 100644 index 0000000000..5b12ea9098 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceOptions.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Used when calling EventSource.Write. + /// Optional overrides for event settings such as Level, Keywords, or Opcode. + /// If overrides are not provided for a setting, default values will be used. + /// </summary> + public struct EventSourceOptions + { + internal EventKeywords keywords; + internal EventTags tags; + internal EventActivityOptions activityOptions; + internal byte level; + internal byte opcode; + internal byte valuesSet; + + internal const byte keywordsSet = 0x1; + internal const byte tagsSet = 0x2; + internal const byte levelSet = 0x4; + internal const byte opcodeSet = 0x8; + internal const byte activityOptionsSet = 0x10; + + /// <summary> + /// Gets or sets the level to use for the specified event. If this property + /// is unset, the event's level will be 5 (Verbose). + /// </summary> + public EventLevel Level + { + get + { + return (EventLevel)this.level; + } + + set + { + this.level = checked((byte)value); + this.valuesSet |= levelSet; + } + } + + /// <summary> + /// Gets or sets the opcode to use for the specified event. If this property + /// is unset, the event's opcode will 0 (Info). + /// </summary> + public EventOpcode Opcode + { + get + { + return (EventOpcode)this.opcode; + } + + set + { + this.opcode = checked((byte)value); + this.valuesSet |= opcodeSet; + } + } + + internal bool IsOpcodeSet + { + get + { + return (this.valuesSet & opcodeSet) != 0; + } + } + + /// <summary> + /// Gets or sets the keywords to use for the specified event. If this + /// property is unset, the event's keywords will be 0. + /// </summary> + public EventKeywords Keywords + { + get + { + return this.keywords; + } + + set + { + this.keywords = value; + this.valuesSet |= keywordsSet; + } + } + + /// <summary> + /// Gets or sets the tags to use for the specified event. If this property is + /// unset, the event's tags will be 0. + /// </summary> + public EventTags Tags + { + get + { + return this.tags; + } + + set + { + this.tags = value; + this.valuesSet |= tagsSet; + } + } + + /// <summary> + /// Gets or sets the activity options for this specified events. If this property is + /// unset, the event's activity options will be 0. + /// </summary> + public EventActivityOptions ActivityOptions + { + get + { + return this.activityOptions; + } + set + { + this.activityOptions = value; + this.valuesSet |= activityOptionsSet; + } + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/FieldMetadata.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/FieldMetadata.cs new file mode 100644 index 0000000000..8dbe604767 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/FieldMetadata.cs @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using Encoding = System.Text.Encoding; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Contains the information needed to generate tracelogging + /// metadata for an event field. + /// </summary> + internal class FieldMetadata + { + /// <summary> + /// Name of the field + /// </summary> + private readonly string name; + + /// <summary> + /// The number of bytes in the UTF8 Encoding of 'name' INCLUDING a null terminator. + /// </summary> + private readonly int nameSize; + private readonly EventFieldTags tags; + private readonly byte[] custom; + + /// <summary> + /// ETW supports fixed sized arrays. If inType has the InTypeFixedCountFlag then this is the + /// statically known count for the array. It is also used to encode the number of bytes of + /// custom meta-data if InTypeCustomCountFlag set. + /// </summary> + private readonly ushort fixedCount; + + private byte inType; + private byte outType; + + /// <summary> + /// Scalar or variable-length array. + /// </summary> + public FieldMetadata( + string name, + TraceLoggingDataType type, + EventFieldTags tags, + bool variableCount) + : this( + name, + type, + tags, + variableCount ? Statics.InTypeVariableCountFlag : (byte)0, + 0, + null) + { + return; + } + + /// <summary> + /// Fixed-length array. + /// </summary> + public FieldMetadata( + string name, + TraceLoggingDataType type, + EventFieldTags tags, + ushort fixedCount) + : this( + name, + type, + tags, + Statics.InTypeFixedCountFlag, + fixedCount, + null) + { + return; + } + + /// <summary> + /// Custom serializer + /// </summary> + public FieldMetadata( + string name, + TraceLoggingDataType type, + EventFieldTags tags, + byte[] custom) + : this( + name, + type, + tags, + Statics.InTypeCustomCountFlag, + checked((ushort)(custom == null ? 0 : custom.Length)), + custom) + { + return; + } + + private FieldMetadata( + string name, + TraceLoggingDataType dataType, + EventFieldTags tags, + byte countFlags, + ushort fixedCount = 0, + byte[] custom = null) + { + if (name == null) + { + throw new ArgumentNullException( + "name", + "This usually means that the object passed to Write is of a type that" + + " does not support being used as the top-level object in an event," + + " e.g. a primitive or built-in type."); + } + + Statics.CheckName(name); + var coreType = (int)dataType & Statics.InTypeMask; + this.name = name; + this.nameSize = Encoding.UTF8.GetByteCount(this.name) + 1; + this.inType = (byte)(coreType | countFlags); + this.outType = (byte)(((int)dataType >> 8) & Statics.OutTypeMask); + this.tags = tags; + this.fixedCount = fixedCount; + this.custom = custom; + + if (countFlags != 0) + { + if (coreType == (int)TraceLoggingDataType.Nil) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNil")); + } + if (coreType == (int)TraceLoggingDataType.Binary) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfBinary")); + } +#if !BROKEN_UNTIL_M3 + if (coreType == (int)TraceLoggingDataType.Utf16String || + coreType == (int)TraceLoggingDataType.MbcsString) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNullTerminatedString")); + } +#endif + } + + if (((int)this.tags & 0xfffffff) != 0) + { + this.outType |= Statics.OutTypeChainFlag; + } + + if (this.outType != 0) + { + this.inType |= Statics.InTypeChainFlag; + } + } + + public void IncrementStructFieldCount() + { + this.inType |= Statics.InTypeChainFlag; + this.outType++; + if ((this.outType & Statics.OutTypeMask) == 0) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_TooManyFields")); + } + } + + /// <summary> + /// This is the main routine for FieldMetaData. Basically it will serialize the data in + /// this structure as TraceLogging style meta-data into the array 'metaArray' starting at + /// 'pos' (pos is updated to reflect the bytes written). + /// + /// Note that 'metaData' can be null, in which case it only updates 'pos'. This is useful + /// for a 'two pass' approach where you figure out how big to make the array, and then you + /// fill it in. + /// </summary> + public void Encode(ref int pos, byte[] metadata) + { + // Write out the null terminated UTF8 encoded name + if (metadata != null) + { + Encoding.UTF8.GetBytes(this.name, 0, this.name.Length, metadata, pos); + } + pos += this.nameSize; + + // Write 1 byte for inType + if (metadata != null) + { + metadata[pos] = this.inType; + } + pos += 1; + + // If InTypeChainFlag set, then write out the outType + if (0 != (this.inType & Statics.InTypeChainFlag)) + { + if (metadata != null) + { + metadata[pos] = this.outType; + } + pos += 1; + + // If OutTypeChainFlag set, then write out tags + if (0 != (this.outType & Statics.OutTypeChainFlag)) + { + Statics.EncodeTags((int)this.tags, ref pos, metadata); + } + } + + // If InTypeFixedCountFlag set, write out the fixedCount (2 bytes little endian) + if (0 != (this.inType & Statics.InTypeFixedCountFlag)) + { + if (metadata != null) + { + metadata[pos + 0] = unchecked((byte)this.fixedCount); + metadata[pos + 1] = (byte)(this.fixedCount >> 8); + } + pos += 2; + + // If InTypeCustomCountFlag set, write out the blob of custom meta-data. + if (Statics.InTypeCustomCountFlag == (this.inType & Statics.InTypeCountMask) && + this.fixedCount != 0) + { + if (metadata != null) + { + Buffer.BlockCopy(this.custom, 0, metadata, pos, this.fixedCount); + } + pos += this.fixedCount; + } + } + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/InvokeTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/InvokeTypeInfo.cs new file mode 100644 index 0000000000..ec4196efa5 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/InvokeTypeInfo.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: An implementation of TraceLoggingTypeInfo that works + /// for arbitrary types. It writes all public instance properties of + /// the type. Implemented using Delegate.CreateDelegate(property.Getter). + /// </summary> + /// <typeparam name="ContainerType"> + /// Type from which to read values. + /// </typeparam> + internal sealed class InvokeTypeInfo<ContainerType> + : TraceLoggingTypeInfo<ContainerType> + { + private readonly PropertyAnalysis[] properties; + private readonly PropertyAccessor<ContainerType>[] accessors; + + public InvokeTypeInfo( + TypeAnalysis typeAnalysis) + : base( + typeAnalysis.name, + typeAnalysis.level, + typeAnalysis.opcode, + typeAnalysis.keywords, + typeAnalysis.tags) + { + if (typeAnalysis.properties.Length != 0) + { + this.properties = typeAnalysis.properties; + this.accessors = new PropertyAccessor<ContainerType>[this.properties.Length]; + for (int i = 0; i < this.accessors.Length; i++) + { + this.accessors[i] = PropertyAccessor<ContainerType>.Create(this.properties[i]); + } + } + } + + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + var groupCollector = collector.AddGroup(name); + if (this.properties != null) + { + foreach (var property in this.properties) + { + var propertyFormat = EventFieldFormat.Default; + var propertyAttribute = property.fieldAttribute; + if (propertyAttribute != null) + { + groupCollector.Tags = propertyAttribute.Tags; + propertyFormat = propertyAttribute.Format; + } + + property.typeInfo.WriteMetadata( + groupCollector, + property.name, + propertyFormat); + } + } + } + + public override void WriteData( + TraceLoggingDataCollector collector, + ref ContainerType value) + { + if (this.accessors != null) + { + foreach (var accessor in this.accessors) + { + accessor.Write(collector, ref value); + } + } + } + + public override object GetData(object value) + { + if (this.properties != null) + { + var membersNames = new List<string>(); + var memebersValues = new List<object>(); + for (int i = 0; i < this.properties.Length; i++) + { + var propertyValue = accessors[i].GetData((ContainerType)value); + membersNames.Add(properties[i].name); + memebersValues.Add(properties[i].typeInfo.GetData(propertyValue)); + } + return new EventPayload(membersNames, memebersValues); + } + + return null; + } + + public override void WriteObjectData( + TraceLoggingDataCollector collector, + object valueObj) + { + if (this.accessors != null) + { + var value = valueObj == null + ? default(ContainerType) + : (ContainerType)valueObj; + this.WriteData(collector, ref value); + } + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/NameInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/NameInfo.cs new file mode 100644 index 0000000000..1820443092 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/NameInfo.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using Interlocked = System.Threading.Interlocked; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Stores the metadata and event identifier corresponding + /// to a tracelogging event type+name+tags combination. + /// </summary> + internal sealed class NameInfo + : ConcurrentSetItem<KeyValuePair<string, EventTags>, NameInfo> + { + private static int lastIdentity = Statics.TraceLoggingChannel << 24; + internal readonly string name; + internal readonly EventTags tags; + internal readonly int identity; + internal readonly byte[] nameMetadata; + + public NameInfo(string name, EventTags tags, int typeMetadataSize) + { + this.name = name; + this.tags = tags & Statics.EventTagsMask; + this.identity = Interlocked.Increment(ref lastIdentity); + + int tagsPos = 0; + Statics.EncodeTags((int)this.tags, ref tagsPos, null); + + this.nameMetadata = Statics.MetadataForString(name, tagsPos, 0, typeMetadataSize); + + tagsPos = 2; + Statics.EncodeTags((int)this.tags, ref tagsPos, this.nameMetadata); + } + + public override int Compare(NameInfo other) + { + return this.Compare(other.name, other.tags); + } + + public override int Compare(KeyValuePair<string, EventTags> key) + { + return this.Compare(key.Key, key.Value & Statics.EventTagsMask); + } + + private int Compare(string otherName, EventTags otherTags) + { + int result = StringComparer.Ordinal.Compare(this.name, otherName); + if (result == 0 && this.tags != otherTags) + { + result = this.tags < otherTags ? -1 : 1; + } + return result; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAccessor.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAccessor.cs new file mode 100644 index 0000000000..7fb7802435 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAccessor.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Reflection; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Each PropertyAccessor instance encapsulates the information + /// needed to read a particular property from an instance of ContainerType + /// and write the value to a DataCollector. Used by InvokeTypeInfo. + /// </summary> + /// <typeparam name="ContainerType"> + /// The type of the object from which properties are read. + /// </typeparam> + internal abstract class PropertyAccessor<ContainerType> + { + public abstract void Write(TraceLoggingDataCollector collector, ref ContainerType value); + public abstract object GetData(ContainerType value); + + public static PropertyAccessor<ContainerType> Create(PropertyAnalysis property) + { + // Due to current Project N limitations on handling generic instantiations with + // 2 generic parameters we have to explicitly create the instantiations that we consider + // important to EventSource performance (we have considered int, long, string for the moment). + // Everything else is handled by NonGenericPropertyWriter that ends up boxing the container object. + var retType = property.getterInfo.ReturnType; + if (!Statics.IsValueType(typeof(ContainerType))) + { + if (retType == typeof(int)) + return new ClassPropertyWriter<ContainerType, int>(property); + else if (retType == typeof(long)) + return new ClassPropertyWriter<ContainerType, long>(property); + else if (retType == typeof(string)) + return new ClassPropertyWriter<ContainerType, string>(property); + } + else + { + // Handle the case if it is a struct (DD 1027919) + } + + // Otherwise use the boxing one. + return new NonGenericProperytWriter<ContainerType>(property); + } + } + + /// <summary> + /// The type specific version of the property writers uses generics in a way + /// that Project N can't handle at the moment. To avoid this we simply + /// use reflection completely. + /// </summary> + internal class NonGenericProperytWriter<ContainerType> : PropertyAccessor<ContainerType> + { + public NonGenericProperytWriter(PropertyAnalysis property) + { + getterInfo = property.getterInfo; + typeInfo = property.typeInfo; + } + + public override void Write(TraceLoggingDataCollector collector, ref ContainerType container) + { + object value = container == null + ? null + : getterInfo.Invoke((object)container, null); + this.typeInfo.WriteObjectData(collector, value); + } + + public override object GetData(ContainerType container) + { + return container == null + ? default(ValueType) + : getterInfo.Invoke((object)container, null); + } + + private readonly TraceLoggingTypeInfo typeInfo; + private readonly MethodInfo getterInfo; + } + + /// <summary> + /// Implementation of PropertyAccessor for use when ContainerType is a + /// value type. + /// </summary> + /// <typeparam name="ContainerType">The type of the object from which properties are read.</typeparam> + /// <typeparam name="ValueType">Type of the property being read.</typeparam> + internal class StructPropertyWriter<ContainerType, ValueType> + : PropertyAccessor<ContainerType> + { + private delegate ValueType Getter(ref ContainerType container); + private readonly TraceLoggingTypeInfo<ValueType> valueTypeInfo; + private readonly Getter getter; + + public StructPropertyWriter(PropertyAnalysis property) + { + this.valueTypeInfo = (TraceLoggingTypeInfo<ValueType>)property.typeInfo; + this.getter = (Getter)Statics.CreateDelegate( + typeof(Getter), + property.getterInfo); + } + + public override void Write(TraceLoggingDataCollector collector, ref ContainerType container) + { + var value = container == null + ? default(ValueType) + : getter(ref container); + this.valueTypeInfo.WriteData(collector, ref value); + } + + public override object GetData(ContainerType container) + { + return container == null + ? default(ValueType) + : getter(ref container); + } + } + + /// <summary> + /// Implementation of PropertyAccessor for use when ContainerType is a + /// reference type. + /// </summary> + /// <typeparam name="ContainerType">The type of the object from which properties are read.</typeparam> + /// <typeparam name="ValueType">Type of the property being read.</typeparam> + internal class ClassPropertyWriter<ContainerType, ValueType> + : PropertyAccessor<ContainerType> + { + private delegate ValueType Getter(ContainerType container); + private readonly TraceLoggingTypeInfo<ValueType> valueTypeInfo; + private readonly Getter getter; + + public ClassPropertyWriter(PropertyAnalysis property) + { + this.valueTypeInfo = (TraceLoggingTypeInfo<ValueType>)property.typeInfo; + this.getter = (Getter)Statics.CreateDelegate( + typeof(Getter), + property.getterInfo); + } + + public override void Write(TraceLoggingDataCollector collector, ref ContainerType container) + { + var value = container == null + ? default(ValueType) + : getter(container); + this.valueTypeInfo.WriteData(collector, ref value); + } + + public override object GetData(ContainerType container) + { + return container == null + ? default(ValueType) + : getter(container); + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAnalysis.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAnalysis.cs new file mode 100644 index 0000000000..523d6830ef --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAnalysis.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Reflection; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: stores the per-property information obtained by + /// reflecting over a type. + /// </summary> + internal sealed class PropertyAnalysis + { + internal readonly string name; + internal readonly MethodInfo getterInfo; + internal readonly TraceLoggingTypeInfo typeInfo; + internal readonly EventFieldAttribute fieldAttribute; + + public PropertyAnalysis( + string name, + MethodInfo getterInfo, + TraceLoggingTypeInfo typeInfo, + EventFieldAttribute fieldAttribute) + { + this.name = name; + this.getterInfo = getterInfo; + this.typeInfo = typeInfo; + this.fieldAttribute = fieldAttribute; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleEventTypes.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleEventTypes.cs new file mode 100644 index 0000000000..9685885879 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleEventTypes.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using Interlocked = System.Threading.Interlocked; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Contains the metadata needed to emit an event, optimized + /// for events with one top-level compile-time-typed payload object. + /// </summary> + /// <typeparam name="T"> + /// Type of the top-level payload object. Should be EmptyStruct if the + /// event has no payload. + /// </typeparam> + internal class SimpleEventTypes<T> + : TraceLoggingEventTypes + { + private static SimpleEventTypes<T> instance; + + internal readonly TraceLoggingTypeInfo<T> typeInfo; + + private SimpleEventTypes(TraceLoggingTypeInfo<T> typeInfo) + : base( + typeInfo.Name, + typeInfo.Tags, + new TraceLoggingTypeInfo[] { typeInfo }) + { + this.typeInfo = typeInfo; + } + + public static SimpleEventTypes<T> Instance + { + get { return instance ?? InitInstance(); } + } + + private static SimpleEventTypes<T> InitInstance() + { + var newInstance = new SimpleEventTypes<T>(TraceLoggingTypeInfo<T>.Instance); + Interlocked.CompareExchange(ref instance, newInstance, null); + return instance; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleTypeInfos.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleTypeInfos.cs new file mode 100644 index 0000000000..87798d8878 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleTypeInfos.cs @@ -0,0 +1,1046 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + #region NullTypeInfo + + /// <summary> + /// TraceLogging: Type handler for empty or unsupported types. + /// </summary> + /// <typeparam name="DataType">The type to handle.</typeparam> + internal sealed class NullTypeInfo<DataType> + : TraceLoggingTypeInfo<DataType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddGroup(name); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref DataType value) + { + return; + } + + public override object GetData(object value) + { + return null; + } + } + + #endregion + + #region Primitive scalars + + /// <summary> + /// TraceLogging: Type handler for Boolean. + /// </summary> + internal sealed class BooleanTypeInfo + : TraceLoggingTypeInfo<Boolean> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format8(format, TraceLoggingDataType.Boolean8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Boolean value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Byte. + /// </summary> + internal sealed class ByteTypeInfo + : TraceLoggingTypeInfo<Byte> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format8(format, TraceLoggingDataType.UInt8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Byte value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for SByte. + /// </summary> + internal sealed class SByteTypeInfo + : TraceLoggingTypeInfo<SByte> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format8(format, TraceLoggingDataType.Int8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref SByte value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int16. + /// </summary> + internal sealed class Int16TypeInfo + : TraceLoggingTypeInfo<Int16> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format16(format, TraceLoggingDataType.Int16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int16 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt16. + /// </summary> + internal sealed class UInt16TypeInfo + : TraceLoggingTypeInfo<UInt16> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format16(format, TraceLoggingDataType.UInt16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt16 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int32. + /// </summary> + internal sealed class Int32TypeInfo + : TraceLoggingTypeInfo<Int32> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format32(format, TraceLoggingDataType.Int32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int32 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt32. + /// </summary> + internal sealed class UInt32TypeInfo + : TraceLoggingTypeInfo<UInt32> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format32(format, TraceLoggingDataType.UInt32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt32 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int64. + /// </summary> + internal sealed class Int64TypeInfo + : TraceLoggingTypeInfo<Int64> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format64(format, TraceLoggingDataType.Int64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int64 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt64. + /// </summary> + internal sealed class UInt64TypeInfo + : TraceLoggingTypeInfo<UInt64> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format64(format, TraceLoggingDataType.UInt64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt64 value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for IntPtr. + /// </summary> + internal sealed class IntPtrTypeInfo + : TraceLoggingTypeInfo<IntPtr> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.FormatPtr(format, Statics.IntPtrType)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref IntPtr value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UIntPtr. + /// </summary> + internal sealed class UIntPtrTypeInfo + : TraceLoggingTypeInfo<UIntPtr> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.FormatPtr(format, Statics.UIntPtrType)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UIntPtr value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Double. + /// </summary> + internal sealed class DoubleTypeInfo + : TraceLoggingTypeInfo<Double> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format64(format, TraceLoggingDataType.Double)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Double value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Single. + /// </summary> + internal sealed class SingleTypeInfo + : TraceLoggingTypeInfo<Single> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format32(format, TraceLoggingDataType.Float)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Single value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Char. + /// </summary> + internal sealed class CharTypeInfo + : TraceLoggingTypeInfo<Char> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format16(format, TraceLoggingDataType.Char16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Char value) + { + collector.AddScalar(value); + } + } + + #endregion + + #region Primitive arrays + + /// <summary> + /// TraceLogging: Type handler for Boolean[]. + /// </summary> + internal sealed class BooleanArrayTypeInfo + : TraceLoggingTypeInfo<Boolean[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format8(format, TraceLoggingDataType.Boolean8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Boolean[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Byte[]. + /// </summary> + internal sealed class ByteArrayTypeInfo + : TraceLoggingTypeInfo<Byte[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + switch (format) + { + default: + collector.AddBinary(name, Statics.MakeDataType(TraceLoggingDataType.Binary, format)); + break; + case EventFieldFormat.String: + collector.AddBinary(name, TraceLoggingDataType.CountedMbcsString); + break; + case EventFieldFormat.Xml: + collector.AddBinary(name, TraceLoggingDataType.CountedMbcsXml); + break; + case EventFieldFormat.Json: + collector.AddBinary(name, TraceLoggingDataType.CountedMbcsJson); + break; + case EventFieldFormat.Boolean: + collector.AddArray(name, TraceLoggingDataType.Boolean8); + break; + case EventFieldFormat.Hexadecimal: + collector.AddArray(name, TraceLoggingDataType.HexInt8); + break; +#if false + case EventSourceFieldFormat.Signed: + collector.AddArray(name, TraceLoggingDataType.Int8); + break; + case EventSourceFieldFormat.Unsigned: + collector.AddArray(name, TraceLoggingDataType.UInt8); + break; +#endif + } + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Byte[] value) + { + collector.AddBinary(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for SByte[]. + /// </summary> + internal sealed class SByteArrayTypeInfo + : TraceLoggingTypeInfo<SByte[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format8(format, TraceLoggingDataType.Int8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref SByte[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int16[]. + /// </summary> + internal sealed class Int16ArrayTypeInfo + : TraceLoggingTypeInfo<Int16[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format16(format, TraceLoggingDataType.Int16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int16[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt16[]. + /// </summary> + internal sealed class UInt16ArrayTypeInfo + : TraceLoggingTypeInfo<UInt16[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format16(format, TraceLoggingDataType.UInt16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt16[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int32[]. + /// </summary> + internal sealed class Int32ArrayTypeInfo + : TraceLoggingTypeInfo<Int32[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format32(format, TraceLoggingDataType.Int32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int32[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt32[]. + /// </summary> + internal sealed class UInt32ArrayTypeInfo + : TraceLoggingTypeInfo<UInt32[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format32(format, TraceLoggingDataType.UInt32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt32[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Int64[]. + /// </summary> + internal sealed class Int64ArrayTypeInfo + : TraceLoggingTypeInfo<Int64[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format64(format, TraceLoggingDataType.Int64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Int64[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UInt64[]. + /// </summary> + internal sealed class UInt64ArrayTypeInfo + : TraceLoggingTypeInfo<UInt64[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format64(format, TraceLoggingDataType.UInt64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UInt64[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for IntPtr[]. + /// </summary> + internal sealed class IntPtrArrayTypeInfo + : TraceLoggingTypeInfo<IntPtr[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.FormatPtr(format, Statics.IntPtrType)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref IntPtr[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for UIntPtr[]. + /// </summary> + internal sealed class UIntPtrArrayTypeInfo + : TraceLoggingTypeInfo<UIntPtr[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.FormatPtr(format, Statics.UIntPtrType)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref UIntPtr[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Char[]. + /// </summary> + internal sealed class CharArrayTypeInfo + : TraceLoggingTypeInfo<Char[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format16(format, TraceLoggingDataType.Char16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Char[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Double[]. + /// </summary> + internal sealed class DoubleArrayTypeInfo + : TraceLoggingTypeInfo<Double[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format64(format, TraceLoggingDataType.Double)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Double[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Single[]. + /// </summary> + internal sealed class SingleArrayTypeInfo + : TraceLoggingTypeInfo<Single[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.Format32(format, TraceLoggingDataType.Float)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Single[] value) + { + collector.AddArray(value); + } + } + + #endregion + + #region Enum scalars + + internal sealed class EnumByteTypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format8(format, TraceLoggingDataType.UInt8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<Byte>.Cast(value)); + } + + public override object GetData(object value) + { + return (Byte)value; + } + } + + internal sealed class EnumSByteTypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format8(format, TraceLoggingDataType.Int8)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<SByte>.Cast(value)); + } + + public override object GetData(object value) + { + return (SByte)value; + } + } + + internal sealed class EnumInt16TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format16(format, TraceLoggingDataType.Int16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<Int16>.Cast(value)); + } + + public override object GetData(object value) + { + return (Int16)value; + } + } + + internal sealed class EnumUInt16TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format16(format, TraceLoggingDataType.UInt16)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<UInt16>.Cast(value)); + } + + public override object GetData(object value) + { + return (UInt16)value; + } + } + + internal sealed class EnumInt32TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format32(format, TraceLoggingDataType.Int32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<Int32>.Cast(value)); + } + + public override object GetData(object value) + { + return (Int32)value; + } + } + + internal sealed class EnumUInt32TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format32(format, TraceLoggingDataType.UInt32)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<UInt32>.Cast(value)); + } + + public override object GetData(object value) + { + return (UInt32)value; + } + } + + internal sealed class EnumInt64TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format64(format, TraceLoggingDataType.Int64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<Int64>.Cast(value)); + } + + public override object GetData(object value) + { + return (Int64)value; + } + } + + internal sealed class EnumUInt64TypeInfo<EnumType> + : TraceLoggingTypeInfo<EnumType> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.Format64(format, TraceLoggingDataType.UInt64)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref EnumType value) + { + collector.AddScalar(EnumHelper<UInt64>.Cast(value)); + } + + public override object GetData(object value) + { + return (UInt64)value; + } + } + + #endregion + + #region Other built-in types + + /// <summary> + /// TraceLogging: Type handler for String. + /// </summary> + internal sealed class StringTypeInfo + : TraceLoggingTypeInfo<String> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddBinary(name, Statics.MakeDataType(TraceLoggingDataType.CountedUtf16String, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref String value) + { + collector.AddBinary(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Guid. + /// </summary> + internal sealed class GuidTypeInfo + : TraceLoggingTypeInfo<Guid> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.Guid, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Guid value) + { + collector.AddScalar(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for Guid[]. + /// </summary> + internal sealed class GuidArrayTypeInfo + : TraceLoggingTypeInfo<Guid[]> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddArray(name, Statics.MakeDataType(TraceLoggingDataType.Guid, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref Guid[] value) + { + collector.AddArray(value); + } + } + + /// <summary> + /// TraceLogging: Type handler for DateTime. + /// </summary> + internal sealed class DateTimeTypeInfo + : TraceLoggingTypeInfo<DateTime> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.FileTime, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref DateTime value) + { + var ticks = value.Ticks; + collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000); + } + } + + /// <summary> + /// TraceLogging: Type handler for DateTimeOffset. + /// </summary> + internal sealed class DateTimeOffsetTypeInfo + : TraceLoggingTypeInfo<DateTimeOffset> + { + public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format) + { + var group = collector.AddGroup(name); + group.AddScalar("Ticks", Statics.MakeDataType(TraceLoggingDataType.FileTime, format)); + group.AddScalar("Offset", TraceLoggingDataType.Int64); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref DateTimeOffset value) + { + var ticks = value.Ticks; + collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000); + collector.AddScalar(value.Offset.Ticks); + } + } + + /// <summary> + /// TraceLogging: Type handler for TimeSpan. + /// </summary> + internal sealed class TimeSpanTypeInfo + : TraceLoggingTypeInfo<TimeSpan> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.Int64, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref TimeSpan value) + { + collector.AddScalar(value.Ticks); + } + } + + /// <summary> + /// TraceLogging: Type handler for Decimal. (Note: not full-fidelity, exposed as Double.) + /// </summary> + internal sealed class DecimalTypeInfo + : TraceLoggingTypeInfo<Decimal> + { + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.Double, format)); + } + + public override void WriteData(TraceLoggingDataCollector collector, ref decimal value) + { + collector.AddScalar((double)value); + } + } + + /// <summary> + /// TraceLogging: Type handler for KeyValuePair. + /// </summary> + /// <typeparam name="K">Type of the KeyValuePair's Key property.</typeparam> + /// <typeparam name="V">Type of the KeyValuePair's Value property.</typeparam> + internal sealed class KeyValuePairTypeInfo<K, V> + : TraceLoggingTypeInfo<KeyValuePair<K, V>> + { + private readonly TraceLoggingTypeInfo<K> keyInfo; + private readonly TraceLoggingTypeInfo<V> valueInfo; + + public KeyValuePairTypeInfo(List<Type> recursionCheck) + { + this.keyInfo = TraceLoggingTypeInfo<K>.GetInstance(recursionCheck); + this.valueInfo = TraceLoggingTypeInfo<V>.GetInstance(recursionCheck); + } + + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + var group = collector.AddGroup(name); + this.keyInfo.WriteMetadata(group, "Key", EventFieldFormat.Default); + this.valueInfo.WriteMetadata(group, "Value", format); + } + + public override void WriteData( + TraceLoggingDataCollector collector, + ref KeyValuePair<K, V> value) + { + var key = value.Key; + var val = value.Value; + this.keyInfo.WriteData(collector, ref key); + this.valueInfo.WriteData(collector, ref val); + } + + public override object GetData(object value) + { + var serializedType = new Dictionary<string, object>(); + var keyValuePair = (KeyValuePair<K, V>) value; + serializedType.Add("Key", this.keyInfo.GetData(keyValuePair.Key)); + serializedType.Add("Value", this.valueInfo.GetData(keyValuePair.Value)); + return serializedType; + } + } + + /// <summary> + /// TraceLogging: Type handler for Nullable. + /// </summary> + /// <typeparam name="T">Type of the Nullable's Value property.</typeparam> + internal sealed class NullableTypeInfo<T> + : TraceLoggingTypeInfo<Nullable<T>> + where T : struct + { + private readonly TraceLoggingTypeInfo<T> valueInfo; + + public NullableTypeInfo(List<Type> recursionCheck) + { + this.valueInfo = TraceLoggingTypeInfo<T>.GetInstance(recursionCheck); + } + + public override void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format) + { + var group = collector.AddGroup(name); + group.AddScalar("HasValue", TraceLoggingDataType.Boolean8); + this.valueInfo.WriteMetadata(group, "Value", format); + } + + public override void WriteData( + TraceLoggingDataCollector collector, + ref Nullable<T> value) + { + var hasValue = value.HasValue; + collector.AddScalar(hasValue); + var val = hasValue ? value.Value : default(T); + this.valueInfo.WriteData(collector, ref val); + } + } + + #endregion +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/Statics.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/Statics.cs new file mode 100644 index 0000000000..5239c31402 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/Statics.cs @@ -0,0 +1,829 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Encoding = System.Text.Encoding; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Constants and utility functions. + /// </summary> + internal static class Statics + { + #region Constants + + public const byte DefaultLevel = 5; + public const byte TraceLoggingChannel = 0xb; + public const byte InTypeMask = 31; + public const byte InTypeFixedCountFlag = 32; + public const byte InTypeVariableCountFlag = 64; + public const byte InTypeCustomCountFlag = 96; + public const byte InTypeCountMask = 96; + public const byte InTypeChainFlag = 128; + public const byte OutTypeMask = 127; + public const byte OutTypeChainFlag = 128; + public const EventTags EventTagsMask = (EventTags)0xfffffff; + + public static readonly TraceLoggingDataType IntPtrType = IntPtr.Size == 8 + ? TraceLoggingDataType.Int64 + : TraceLoggingDataType.Int32; + public static readonly TraceLoggingDataType UIntPtrType = IntPtr.Size == 8 + ? TraceLoggingDataType.UInt64 + : TraceLoggingDataType.UInt32; + public static readonly TraceLoggingDataType HexIntPtrType = IntPtr.Size == 8 + ? TraceLoggingDataType.HexInt64 + : TraceLoggingDataType.HexInt32; + + #endregion + + #region Metadata helpers + + /// <summary> + /// A complete metadata chunk can be expressed as: + /// length16 + prefix + null-terminated-utf8-name + suffix + additionalData. + /// We assume that excludedData will be provided by some other means, + /// but that its size is known. This function returns a blob containing + /// length16 + prefix + name + suffix, with prefix and suffix initialized + /// to 0's. The length16 value is initialized to the length of the returned + /// blob plus additionalSize, so that the concatenation of the returned blob + /// plus a blob of size additionalSize constitutes a valid metadata blob. + /// </summary> + /// <param name="name"> + /// The name to include in the blob. + /// </param> + /// <param name="prefixSize"> + /// Amount of space to reserve before name. For provider or field blobs, this + /// should be 0. For event blobs, this is used for the tags field and will vary + /// from 1 to 4, depending on how large the tags field needs to be. + /// </param> + /// <param name="suffixSize"> + /// Amount of space to reserve after name. For example, a provider blob with no + /// traits would reserve 0 extra bytes, but a provider blob with a single GroupId + /// field would reserve 19 extra bytes. + /// </param> + /// <param name="additionalSize"> + /// Amount of additional data in another blob. This value will be counted in the + /// blob's length field, but will not be included in the returned byte[] object. + /// The complete blob would then be the concatenation of the returned byte[] object + /// with another byte[] object of length additionalSize. + /// </param> + /// <returns> + /// A byte[] object with the length and name fields set, with room reserved for + /// prefix and suffix. If additionalSize was 0, the byte[] object is a complete + /// blob. Otherwise, another byte[] of size additionalSize must be concatenated + /// with this one to form a complete blob. + /// </returns> + public static byte[] MetadataForString( + string name, + int prefixSize, + int suffixSize, + int additionalSize) + { + Statics.CheckName(name); + int metadataSize = Encoding.UTF8.GetByteCount(name) + 3 + prefixSize + suffixSize; + var metadata = new byte[metadataSize]; + ushort totalSize = checked((ushort)(metadataSize + additionalSize)); + metadata[0] = unchecked((byte)totalSize); + metadata[1] = unchecked((byte)(totalSize >> 8)); + Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2 + prefixSize); + return metadata; + } + + /// <summary> + /// Serialize the low 28 bits of the tags value into the metadata stream, + /// starting at the index given by pos. Updates pos. Writes 1 to 4 bytes, + /// depending on the value of the tags variable. Usable for event tags and + /// field tags. + /// + /// Note that 'metadata' can be null, in which case it only updates 'pos'. + /// This is useful for a two pass approach where you figure out how big to + /// make the array, and then you fill it in. + /// </summary> + public static void EncodeTags(int tags, ref int pos, byte[] metadata) + { + // We transmit the low 28 bits of tags, high bits first, 7 bits at a time. + var tagsLeft = tags & 0xfffffff; + bool more; + do + { + byte current = (byte)((tagsLeft >> 21) & 0x7f); + more = (tagsLeft & 0x1fffff) != 0; + current |= (byte)(more ? 0x80 : 0x00); + tagsLeft = tagsLeft << 7; + + if (metadata != null) + { + metadata[pos] = current; + } + pos += 1; + } + while (more); + } + + public static byte Combine( + int settingValue, + byte defaultValue) + { + unchecked + { + return (byte)settingValue == settingValue + ? (byte)settingValue + : defaultValue; + } + } + + public static byte Combine( + int settingValue1, + int settingValue2, + byte defaultValue) + { + unchecked + { + return (byte)settingValue1 == settingValue1 + ? (byte)settingValue1 + : (byte)settingValue2 == settingValue2 + ? (byte)settingValue2 + : defaultValue; + } + } + + public static int Combine( + int settingValue1, + int settingValue2) + { + unchecked + { + return (byte)settingValue1 == settingValue1 + ? settingValue1 + : settingValue2; + } + } + + public static void CheckName(string name) + { + if (name != null && 0 <= name.IndexOf('\0')) + { + throw new ArgumentOutOfRangeException("name"); + } + } + + public static bool ShouldOverrideFieldName(string fieldName) + { + return (fieldName.Length <= 2 && fieldName[0] == '_'); + } + + public static TraceLoggingDataType MakeDataType( + TraceLoggingDataType baseType, + EventFieldFormat format) + { + return (TraceLoggingDataType)(((int)baseType & 0x1f) | ((int)format << 8)); + } + + /// <summary> + /// Adjusts the native type based on format. + /// - If format is default, return native. + /// - If format is recognized, return the canonical type for that format. + /// - Otherwise remove existing format from native and apply the requested format. + /// </summary> + public static TraceLoggingDataType Format8( + EventFieldFormat format, + TraceLoggingDataType native) + { + switch (format) + { + case EventFieldFormat.Default: + return native; + case EventFieldFormat.String: + return TraceLoggingDataType.Char8; + case EventFieldFormat.Boolean: + return TraceLoggingDataType.Boolean8; + case EventFieldFormat.Hexadecimal: + return TraceLoggingDataType.HexInt8; +#if false + case EventSourceFieldFormat.Signed: + return TraceLoggingDataType.Int8; + case EventSourceFieldFormat.Unsigned: + return TraceLoggingDataType.UInt8; +#endif + default: + return MakeDataType(native, format); + } + } + + /// <summary> + /// Adjusts the native type based on format. + /// - If format is default, return native. + /// - If format is recognized, return the canonical type for that format. + /// - Otherwise remove existing format from native and apply the requested format. + /// </summary> + public static TraceLoggingDataType Format16( + EventFieldFormat format, + TraceLoggingDataType native) + { + switch (format) + { + case EventFieldFormat.Default: + return native; + case EventFieldFormat.String: + return TraceLoggingDataType.Char16; + case EventFieldFormat.Hexadecimal: + return TraceLoggingDataType.HexInt16; +#if false + case EventSourceFieldFormat.Port: + return TraceLoggingDataType.Port; + case EventSourceFieldFormat.Signed: + return TraceLoggingDataType.Int16; + case EventSourceFieldFormat.Unsigned: + return TraceLoggingDataType.UInt16; +#endif + default: + return MakeDataType(native, format); + } + } + + /// <summary> + /// Adjusts the native type based on format. + /// - If format is default, return native. + /// - If format is recognized, return the canonical type for that format. + /// - Otherwise remove existing format from native and apply the requested format. + /// </summary> + public static TraceLoggingDataType Format32( + EventFieldFormat format, + TraceLoggingDataType native) + { + switch (format) + { + case EventFieldFormat.Default: + return native; + case EventFieldFormat.Boolean: + return TraceLoggingDataType.Boolean32; + case EventFieldFormat.Hexadecimal: + return TraceLoggingDataType.HexInt32; +#if false + case EventSourceFieldFormat.Ipv4Address: + return TraceLoggingDataType.Ipv4Address; + case EventSourceFieldFormat.ProcessId: + return TraceLoggingDataType.ProcessId; + case EventSourceFieldFormat.ThreadId: + return TraceLoggingDataType.ThreadId; + case EventSourceFieldFormat.Win32Error: + return TraceLoggingDataType.Win32Error; + case EventSourceFieldFormat.NTStatus: + return TraceLoggingDataType.NTStatus; +#endif + case EventFieldFormat.HResult: + return TraceLoggingDataType.HResult; +#if false + case EventSourceFieldFormat.Signed: + return TraceLoggingDataType.Int32; + case EventSourceFieldFormat.Unsigned: + return TraceLoggingDataType.UInt32; +#endif + default: + return MakeDataType(native, format); + } + } + + /// <summary> + /// Adjusts the native type based on format. + /// - If format is default, return native. + /// - If format is recognized, return the canonical type for that format. + /// - Otherwise remove existing format from native and apply the requested format. + /// </summary> + public static TraceLoggingDataType Format64( + EventFieldFormat format, + TraceLoggingDataType native) + { + switch (format) + { + case EventFieldFormat.Default: + return native; + case EventFieldFormat.Hexadecimal: + return TraceLoggingDataType.HexInt64; +#if false + case EventSourceFieldFormat.FileTime: + return TraceLoggingDataType.FileTime; + case EventSourceFieldFormat.Signed: + return TraceLoggingDataType.Int64; + case EventSourceFieldFormat.Unsigned: + return TraceLoggingDataType.UInt64; +#endif + default: + return MakeDataType(native, format); + } + } + + /// <summary> + /// Adjusts the native type based on format. + /// - If format is default, return native. + /// - If format is recognized, return the canonical type for that format. + /// - Otherwise remove existing format from native and apply the requested format. + /// </summary> + public static TraceLoggingDataType FormatPtr( + EventFieldFormat format, + TraceLoggingDataType native) + { + switch (format) + { + case EventFieldFormat.Default: + return native; + case EventFieldFormat.Hexadecimal: + return HexIntPtrType; +#if false + case EventSourceFieldFormat.Signed: + return IntPtrType; + case EventSourceFieldFormat.Unsigned: + return UIntPtrType; +#endif + default: + return MakeDataType(native, format); + } + } + + #endregion + + #region Reflection helpers + + /* + All TraceLogging use of reflection APIs should go through wrappers here. + This helps with portability, and it also makes it easier to audit what + kinds of reflection operations are being done. + */ + + public static object CreateInstance(Type type, params object[] parameters) + { + return Activator.CreateInstance(type, parameters); + } + + public static bool IsValueType(Type type) + { + bool result; +#if ES_BUILD_PCL + result = type.GetTypeInfo().IsValueType; +#else + result = type.IsValueType; +#endif + return result; + } + + public static bool IsEnum(Type type) + { + bool result; +#if ES_BUILD_PCL + result = type.GetTypeInfo().IsEnum; +#else + result = type.IsEnum; +#endif + return result; + } + + public static IEnumerable<PropertyInfo> GetProperties(Type type) + { + IEnumerable<PropertyInfo> result; +#if ES_BUILD_PCL + result = type.GetRuntimeProperties(); +#else + result = type.GetProperties(); +#endif + return result; + } + + public static MethodInfo GetGetMethod(PropertyInfo propInfo) + { + MethodInfo result; +#if ES_BUILD_PCL + result = propInfo.GetMethod; +#else + result = propInfo.GetGetMethod(); +#endif + return result; + } + + public static MethodInfo GetDeclaredStaticMethod(Type declaringType, string name) + { + MethodInfo result; +#if ES_BUILD_PCL + result = declaringType.GetTypeInfo().GetDeclaredMethod(name); +#else + result = declaringType.GetMethod( + name, + BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic); +#endif + return result; + } + + public static bool HasCustomAttribute( + PropertyInfo propInfo, + Type attributeType) + { + bool result; +#if ES_BUILD_PCL + result = propInfo.IsDefined(attributeType); +#else + var attributes = propInfo.GetCustomAttributes( + attributeType, + false); + result = attributes.Length != 0; +#endif + return result; + } + + public static AttributeType GetCustomAttribute<AttributeType>(PropertyInfo propInfo) + where AttributeType : Attribute + { + AttributeType result = null; +#if ES_BUILD_PCL + foreach (var attrib in propInfo.GetCustomAttributes<AttributeType>(false)) + { + result = attrib; + break; + } +#else + var attributes = propInfo.GetCustomAttributes(typeof(AttributeType), false); + if (attributes.Length != 0) + { + result = (AttributeType)attributes[0]; + } +#endif + return result; + } + + public static AttributeType GetCustomAttribute<AttributeType>(Type type) + where AttributeType : Attribute + { + AttributeType result = null; +#if ES_BUILD_PCL + foreach (var attrib in type.GetTypeInfo().GetCustomAttributes<AttributeType>(false)) + { + result = attrib; + break; + } +#else + var attributes = type.GetCustomAttributes(typeof(AttributeType), false); + if (attributes.Length != 0) + { + result = (AttributeType)attributes[0]; + } +#endif + return result; + } + + public static Type[] GetGenericArguments(Type type) + { +#if ES_BUILD_PCL + return type.GenericTypeArguments; +#else + return type.GetGenericArguments(); +#endif + } + + public static Type FindEnumerableElementType(Type type) + { + Type elementType = null; + + if (IsGenericMatch(type, typeof(IEnumerable<>))) + { + elementType = GetGenericArguments(type)[0]; + } + else + { +#if ES_BUILD_PCL + var ifaceTypes = type.GetTypeInfo().ImplementedInterfaces; +#else + var ifaceTypes = type.FindInterfaces(IsGenericMatch, typeof(IEnumerable<>)); +#endif + + foreach (var ifaceType in ifaceTypes) + { +#if ES_BUILD_PCL + if (!IsGenericMatch(ifaceType, typeof(IEnumerable<>))) + { + continue; + } +#endif + + if (elementType != null) + { + // ambiguous match. report no match at all. + elementType = null; + break; + } + + elementType = GetGenericArguments(ifaceType)[0]; + } + } + + return elementType; + } + + public static bool IsGenericMatch(Type type, object openType) + { + bool isGeneric; +#if ES_BUILD_PCL + isGeneric = type.IsConstructedGenericType; +#else + isGeneric = type.IsGenericType; +#endif + return isGeneric && type.GetGenericTypeDefinition() == (Type)openType; + } + + public static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo) + { + Delegate result; +#if ES_BUILD_PCL + result = methodInfo.CreateDelegate( + delegateType); +#else + result = Delegate.CreateDelegate( + delegateType, + methodInfo); +#endif + return result; + } + + public static TraceLoggingTypeInfo GetTypeInfoInstance(Type dataType, List<Type> recursionCheck) + { + TraceLoggingTypeInfo result; + + if (dataType == typeof(Int32)) + { + result = TraceLoggingTypeInfo<Int32>.Instance; + } + else if (dataType == typeof(Int64)) + { + result = TraceLoggingTypeInfo<Int64>.Instance; + } + else if (dataType == typeof(String)) + { + result = TraceLoggingTypeInfo<String>.Instance; + } + else + { + var getInstanceInfo = Statics.GetDeclaredStaticMethod( + typeof(TraceLoggingTypeInfo<>).MakeGenericType(dataType), + "GetInstance"); + var typeInfoObj = getInstanceInfo.Invoke(null, new object[] { recursionCheck }); + result = (TraceLoggingTypeInfo)typeInfoObj; + } + + return result; + } + + public static TraceLoggingTypeInfo<DataType> CreateDefaultTypeInfo<DataType>( + List<Type> recursionCheck) + { + TraceLoggingTypeInfo result; + var dataType = typeof(DataType); + + if (recursionCheck.Contains(dataType)) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_RecursiveTypeDefinition")); + } + + recursionCheck.Add(dataType); + + var eventAttrib = Statics.GetCustomAttribute<EventDataAttribute>(dataType); + if (eventAttrib != null || + Statics.GetCustomAttribute<CompilerGeneratedAttribute>(dataType) != null) + { + var analysis = new TypeAnalysis(dataType, eventAttrib, recursionCheck); + result = new InvokeTypeInfo<DataType>(analysis); + } + else if (dataType.IsArray) + { + var elementType = dataType.GetElementType(); + if (elementType == typeof(Boolean)) + { + result = new BooleanArrayTypeInfo(); + } + else if (elementType == typeof(Byte)) + { + result = new ByteArrayTypeInfo(); + } + else if (elementType == typeof(SByte)) + { + result = new SByteArrayTypeInfo(); + } + else if (elementType == typeof(Int16)) + { + result = new Int16ArrayTypeInfo(); + } + else if (elementType == typeof(UInt16)) + { + result = new UInt16ArrayTypeInfo(); + } + else if (elementType == typeof(Int32)) + { + result = new Int32ArrayTypeInfo(); + } + else if (elementType == typeof(UInt32)) + { + result = new UInt32ArrayTypeInfo(); + } + else if (elementType == typeof(Int64)) + { + result = new Int64ArrayTypeInfo(); + } + else if (elementType == typeof(UInt64)) + { + result = new UInt64ArrayTypeInfo(); + } + else if (elementType == typeof(Char)) + { + result = new CharArrayTypeInfo(); + } + else if (elementType == typeof(Double)) + { + result = new DoubleArrayTypeInfo(); + } + else if (elementType == typeof(Single)) + { + result = new SingleArrayTypeInfo(); + } + else if (elementType == typeof(IntPtr)) + { + result = new IntPtrArrayTypeInfo(); + } + else if (elementType == typeof(UIntPtr)) + { + result = new UIntPtrArrayTypeInfo(); + } + else if (elementType == typeof(Guid)) + { + result = new GuidArrayTypeInfo(); + } + else + { + result = (TraceLoggingTypeInfo<DataType>)CreateInstance( + typeof(ArrayTypeInfo<>).MakeGenericType(elementType), + GetTypeInfoInstance(elementType, recursionCheck)); + } + } + else if (Statics.IsEnum(dataType)) + { + var underlyingType = Enum.GetUnderlyingType(dataType); + if (underlyingType == typeof(Int32)) + { + result = new EnumInt32TypeInfo<DataType>(); + } + else if (underlyingType == typeof(UInt32)) + { + result = new EnumUInt32TypeInfo<DataType>(); + } + else if (underlyingType == typeof(Byte)) + { + result = new EnumByteTypeInfo<DataType>(); + } + else if (underlyingType == typeof(SByte)) + { + result = new EnumSByteTypeInfo<DataType>(); + } + else if (underlyingType == typeof(Int16)) + { + result = new EnumInt16TypeInfo<DataType>(); + } + else if (underlyingType == typeof(UInt16)) + { + result = new EnumUInt16TypeInfo<DataType>(); + } + else if (underlyingType == typeof(Int64)) + { + result = new EnumInt64TypeInfo<DataType>(); + } + else if (underlyingType == typeof(UInt64)) + { + result = new EnumUInt64TypeInfo<DataType>(); + } + else + { + // Unexpected + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedEnumType", dataType.Name, underlyingType.Name)); + } + } + else if (dataType == typeof(String)) + { + result = new StringTypeInfo(); + } + else if (dataType == typeof(Boolean)) + { + result = new BooleanTypeInfo(); + } + else if (dataType == typeof(Byte)) + { + result = new ByteTypeInfo(); + } + else if (dataType == typeof(SByte)) + { + result = new SByteTypeInfo(); + } + else if (dataType == typeof(Int16)) + { + result = new Int16TypeInfo(); + } + else if (dataType == typeof(UInt16)) + { + result = new UInt16TypeInfo(); + } + else if (dataType == typeof(Int32)) + { + result = new Int32TypeInfo(); + } + else if (dataType == typeof(UInt32)) + { + result = new UInt32TypeInfo(); + } + else if (dataType == typeof(Int64)) + { + result = new Int64TypeInfo(); + } + else if (dataType == typeof(UInt64)) + { + result = new UInt64TypeInfo(); + } + else if (dataType == typeof(Char)) + { + result = new CharTypeInfo(); + } + else if (dataType == typeof(Double)) + { + result = new DoubleTypeInfo(); + } + else if (dataType == typeof(Single)) + { + result = new SingleTypeInfo(); + } + else if (dataType == typeof(DateTime)) + { + result = new DateTimeTypeInfo(); + } + else if (dataType == typeof(Decimal)) + { + result = new DecimalTypeInfo(); + } + else if (dataType == typeof(IntPtr)) + { + result = new IntPtrTypeInfo(); + } + else if (dataType == typeof(UIntPtr)) + { + result = new UIntPtrTypeInfo(); + } + else if (dataType == typeof(Guid)) + { + result = new GuidTypeInfo(); + } + else if (dataType == typeof(TimeSpan)) + { + result = new TimeSpanTypeInfo(); + } + else if (dataType == typeof(DateTimeOffset)) + { + result = new DateTimeOffsetTypeInfo(); + } + else if (dataType == typeof(EmptyStruct)) + { + result = new NullTypeInfo<EmptyStruct>(); + } + else if (IsGenericMatch(dataType, typeof(KeyValuePair<,>))) + { + var args = GetGenericArguments(dataType); + result = (TraceLoggingTypeInfo<DataType>)CreateInstance( + typeof(KeyValuePairTypeInfo<,>).MakeGenericType(args[0], args[1]), + recursionCheck); + } + else if (IsGenericMatch(dataType, typeof(Nullable<>))) + { + var args = GetGenericArguments(dataType); + result = (TraceLoggingTypeInfo<DataType>)CreateInstance( + typeof(NullableTypeInfo<>).MakeGenericType(args[0]), + recursionCheck); + } + else + { + var elementType = FindEnumerableElementType(dataType); + if (elementType != null) + { + result = (TraceLoggingTypeInfo<DataType>)CreateInstance( + typeof(EnumerableTypeInfo<,>).MakeGenericType(dataType, elementType), + GetTypeInfoInstance(elementType, recursionCheck)); + } + else + { + throw new ArgumentException(Environment.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name)); + } + } + + return (TraceLoggingTypeInfo<DataType>)result; + } + + #endregion + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataCollector.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataCollector.cs new file mode 100644 index 0000000000..4b8158c0d7 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataCollector.cs @@ -0,0 +1,394 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Security; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo. + /// The instance of this type is provided to the TypeInfo.WriteData method. + /// All operations are forwarded to the current thread's DataCollector. + /// Note that this abstraction would allow us to expose the custom + /// serialization system to partially-trusted code. If we end up not + /// making custom serialization public, or if we only expose it to + /// full-trust code, this abstraction is unnecessary (though it probably + /// doesn't hurt anything). + /// </summary> + [SecuritySafeCritical] + internal unsafe class TraceLoggingDataCollector + { + internal static readonly TraceLoggingDataCollector Instance = new TraceLoggingDataCollector(); + + private TraceLoggingDataCollector() + { + return; + } + + /// <summary> + /// Marks the start of a non-blittable array or enumerable. + /// </summary> + /// <returns>Bookmark to be passed to EndBufferedArray.</returns> + public int BeginBufferedArray() + { + return DataCollector.ThreadInstance.BeginBufferedArray(); + } + + /// <summary> + /// Marks the end of a non-blittable array or enumerable. + /// </summary> + /// <param name="bookmark">The value returned by BeginBufferedArray.</param> + /// <param name="count">The number of items in the array.</param> + public void EndBufferedArray(int bookmark, int count) + { + DataCollector.ThreadInstance.EndBufferedArray(bookmark, count); + } + + /// <summary> + /// Adds the start of a group to the event. + /// This has no effect on the event payload, but is provided to allow + /// WriteMetadata and WriteData implementations to have similar + /// sequences of calls, allowing for easier verification of correctness. + /// </summary> + public TraceLoggingDataCollector AddGroup() + { + return this; + } + + /// <summary> + /// Adds a Boolean value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(bool value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(bool)); + } + + /// <summary> + /// Adds an SByte value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + //[CLSCompliant(false)] + public void AddScalar(sbyte value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(sbyte)); + } + + /// <summary> + /// Adds a Byte value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(byte value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(byte)); + } + + /// <summary> + /// Adds an Int16 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(short value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(short)); + } + + /// <summary> + /// Adds a UInt16 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + //[CLSCompliant(false)] + public void AddScalar(ushort value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(ushort)); + } + + /// <summary> + /// Adds an Int32 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(int value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(int)); + } + + /// <summary> + /// Adds a UInt32 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + //[CLSCompliant(false)] + public void AddScalar(uint value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(uint)); + } + + /// <summary> + /// Adds an Int64 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(long value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(long)); + } + + /// <summary> + /// Adds a UInt64 value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + //[CLSCompliant(false)] + public void AddScalar(ulong value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(ulong)); + } + + /// <summary> + /// Adds an IntPtr value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(IntPtr value) + { + DataCollector.ThreadInstance.AddScalar(&value, IntPtr.Size); + } + + /// <summary> + /// Adds a UIntPtr value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + //[CLSCompliant(false)] + public void AddScalar(UIntPtr value) + { + DataCollector.ThreadInstance.AddScalar(&value, UIntPtr.Size); + } + + /// <summary> + /// Adds a Single value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(float value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(float)); + } + + /// <summary> + /// Adds a Double value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(double value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(double)); + } + + /// <summary> + /// Adds a Char value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(char value) + { + DataCollector.ThreadInstance.AddScalar(&value, sizeof(char)); + } + + /// <summary> + /// Adds a Guid value to the event payload. + /// </summary> + /// <param name="value">Value to be added.</param> + public void AddScalar(Guid value) + { + DataCollector.ThreadInstance.AddScalar(&value, 16); + } + + /// <summary> + /// Adds a counted String value to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length string. + /// </param> + public void AddBinary(string value) + { + DataCollector.ThreadInstance.AddBinary(value, value == null ? 0 : value.Length * 2); + } + + /// <summary> + /// Adds an array of Byte values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddBinary(byte[] value) + { + DataCollector.ThreadInstance.AddBinary(value, value == null ? 0 : value.Length); + } + + /// <summary> + /// Adds an array of Boolean values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(bool[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(bool)); + } + + /// <summary> + /// Adds an array of SByte values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + //[CLSCompliant(false)] + public void AddArray(sbyte[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(sbyte)); + } + + /// <summary> + /// Adds an array of Int16 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(short[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(short)); + } + + /// <summary> + /// Adds an array of UInt16 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + //[CLSCompliant(false)] + public void AddArray(ushort[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(ushort)); + } + + /// <summary> + /// Adds an array of Int32 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(int[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(int)); + } + + /// <summary> + /// Adds an array of UInt32 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + //[CLSCompliant(false)] + public void AddArray(uint[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(uint)); + } + + /// <summary> + /// Adds an array of Int64 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(long[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(long)); + } + + /// <summary> + /// Adds an array of UInt64 values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + //[CLSCompliant(false)] + public void AddArray(ulong[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(ulong)); + } + + /// <summary> + /// Adds an array of IntPtr values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(IntPtr[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, IntPtr.Size); + } + + /// <summary> + /// Adds an array of UIntPtr values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + //[CLSCompliant(false)] + public void AddArray(UIntPtr[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, UIntPtr.Size); + } + + /// <summary> + /// Adds an array of Single values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(float[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(float)); + } + + /// <summary> + /// Adds an array of Double values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(double[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(double)); + } + + /// <summary> + /// Adds an array of Char values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(char[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(char)); + } + + /// <summary> + /// Adds an array of Guid values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddArray(Guid[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, 16); + } + + /// <summary> + /// Adds an array of Byte values to the event payload. + /// </summary> + /// <param name="value"> + /// Value to be added. A null value is treated as a zero-length array. + /// </param> + public void AddCustom(byte[] value) + { + DataCollector.ThreadInstance.AddArray(value, value == null ? 0 : value.Length, sizeof(byte)); + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataType.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataType.cs new file mode 100644 index 0000000000..8e218ba9bf --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataType.cs @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo. + /// These are passed to metadataCollector.Add to specify the low-level + /// type of a field in the event payload. Note that a "formatted" + /// TraceLoggingDataType consists of a core TraceLoggingDataType value + /// (a TraceLoggingDataType with a value less than 32) plus an OutType. + /// Any combination of TraceLoggingDataType + OutType is valid, but not + /// all are useful. In particular, combinations not explicitly listed + /// below are unlikely to be recognized by decoders, and will typically + /// be decoded as the corresponding core type (i.e. the decoder will + /// mask off any unrecognized OutType value). + /// </summary> + internal enum TraceLoggingDataType + { + /// <summary> + /// Core type. + /// Data type with no value (0-length payload). + /// NOTE: arrays of Nil are illegal. + /// NOTE: a fixed-length array of Nil is interpreted by the decoder as + /// a struct (obsolete but retained for backwards-compatibility). + /// </summary> + Nil = 0, + + /// <summary> + /// Core type. + /// Encoding assumes null-terminated Char16 string. + /// Decoding treats as UTF-16LE string. + /// </summary> + Utf16String = 1, + + /// <summary> + /// Core type. + /// Encoding assumes null-terminated Char8 string. + /// Decoding treats as MBCS string. + /// </summary> + MbcsString = 2, + + /// <summary> + /// Core type. + /// Encoding assumes 8-bit value. + /// Decoding treats as signed integer. + /// </summary> + Int8 = 3, + + /// <summary> + /// Core type. + /// Encoding assumes 8-bit value. + /// Decoding treats as unsigned integer. + /// </summary> + UInt8 = 4, + + /// <summary> + /// Core type. + /// Encoding assumes 16-bit value. + /// Decoding treats as signed integer. + /// </summary> + Int16 = 5, + + /// <summary> + /// Core type. + /// Encoding assumes 16-bit value. + /// Decoding treats as unsigned integer. + /// </summary> + UInt16 = 6, + + /// <summary> + /// Core type. + /// Encoding assumes 32-bit value. + /// Decoding treats as signed integer. + /// </summary> + Int32 = 7, + + /// <summary> + /// Core type. + /// Encoding assumes 32-bit value. + /// Decoding treats as unsigned integer. + /// </summary> + UInt32 = 8, + + /// <summary> + /// Core type. + /// Encoding assumes 64-bit value. + /// Decoding treats as signed integer. + /// </summary> + Int64 = 9, + + /// <summary> + /// Core type. + /// Encoding assumes 64-bit value. + /// Decoding treats as unsigned integer. + /// </summary> + UInt64 = 10, + + /// <summary> + /// Core type. + /// Encoding assumes 32-bit value. + /// Decoding treats as Float. + /// </summary> + Float = 11, + + /// <summary> + /// Core type. + /// Encoding assumes 64-bit value. + /// Decoding treats as Double. + /// </summary> + Double = 12, + + /// <summary> + /// Core type. + /// Encoding assumes 32-bit value. + /// Decoding treats as Boolean. + /// </summary> + Boolean32 = 13, + + /// <summary> + /// Core type. + /// Encoding assumes 16-bit bytecount followed by binary data. + /// Decoding treats as binary data. + /// </summary> + Binary = 14, + + /// <summary> + /// Core type. + /// Encoding assumes 16-byte value. + /// Decoding treats as GUID. + /// </summary> + Guid = 15, + + /// <summary> + /// Core type. + /// Encoding assumes 64-bit value. + /// Decoding treats as FILETIME. + /// </summary> + FileTime = 17, + + /// <summary> + /// Core type. + /// Encoding assumes 16-byte value. + /// Decoding treats as SYSTEMTIME. + /// </summary> + SystemTime = 18, + + /// <summary> + /// Core type. + /// Encoding assumes 32-bit value. + /// Decoding treats as hexadecimal unsigned integer. + /// </summary> + HexInt32 = 20, + + /// <summary> + /// Core type. + /// Encoding assumes 64-bit value. + /// Decoding treats as hexadecimal unsigned integer. + /// </summary> + HexInt64 = 21, + + /// <summary> + /// Core type. + /// Encoding assumes 16-bit bytecount followed by Char16 data. + /// Decoding treats as UTF-16LE string. + /// </summary> + CountedUtf16String = 22, + + /// <summary> + /// Core type. + /// Encoding assumes 16-bit bytecount followed by Char8 data. + /// Decoding treats as MBCS string. + /// </summary> + CountedMbcsString = 23, + + /// <summary> + /// Core type. + /// Special case: Struct indicates that this field plus the the + /// subsequent N logical fields are to be considered as one logical + /// field (i.e. a nested structure). The OutType is used to encode N. + /// The maximum value for N is 127. This field has no payload by + /// itself, but logically contains the payload of the following N + /// fields. It is legal to have an array of Struct. + /// </summary> + Struct = 24, + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit value. + /// Decoding treats as UTF-16LE character. + /// </summary> + Char16 = UInt16 + (EventFieldFormat.String << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 8-bit value. + /// Decoding treats as character. + /// </summary> + Char8 = UInt8 + (EventFieldFormat.String << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 8-bit value. + /// Decoding treats as Boolean. + /// </summary> + Boolean8 = UInt8 + (EventFieldFormat.Boolean << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 8-bit value. + /// Decoding treats as hexadecimal unsigned integer. + /// </summary> + HexInt8 = UInt8 + (EventFieldFormat.Hexadecimal << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit value. + /// Decoding treats as hexadecimal unsigned integer. + /// </summary> + HexInt16 = UInt16 + (EventFieldFormat.Hexadecimal << 8), + +#if false + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as process identifier. + /// </summary> + ProcessId = UInt32 + (EventSourceFieldFormat.ProcessId << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as thread identifier. + /// </summary> + ThreadId = UInt32 + (EventSourceFieldFormat.ThreadId << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit value. + /// Decoding treats as IP port. + /// </summary> + Port = UInt16 + (EventSourceFieldFormat.Port << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as IPv4 address. + /// </summary> + Ipv4Address = UInt32 + (EventSourceFieldFormat.Ipv4Address << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by binary data. + /// Decoding treats as IPv6 address. + /// </summary> + Ipv6Address = Binary + (EventSourceFieldFormat.Ipv6Address << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by binary data. + /// Decoding treats as SOCKADDR. + /// </summary> + SocketAddress = Binary + (EventSourceFieldFormat.SocketAddress << 8), +#endif + /// <summary> + /// Formatted type. + /// Encoding assumes null-terminated Char16 string. + /// Decoding treats as UTF-16LE XML string. + /// </summary> + Utf16Xml = Utf16String + (EventFieldFormat.Xml << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes null-terminated Char8 string. + /// Decoding treats as MBCS XML string. + /// </summary> + MbcsXml = MbcsString + (EventFieldFormat.Xml << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by Char16 data. + /// Decoding treats as UTF-16LE XML. + /// </summary> + CountedUtf16Xml = CountedUtf16String + (EventFieldFormat.Xml << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by Char8 data. + /// Decoding treats as MBCS XML. + /// </summary> + CountedMbcsXml = CountedMbcsString + (EventFieldFormat.Xml << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes null-terminated Char16 string. + /// Decoding treats as UTF-16LE JSON string. + /// </summary> + Utf16Json = Utf16String + (EventFieldFormat.Json << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes null-terminated Char8 string. + /// Decoding treats as MBCS JSON string. + /// </summary> + MbcsJson = MbcsString + (EventFieldFormat.Json << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by Char16 data. + /// Decoding treats as UTF-16LE JSON. + /// </summary> + CountedUtf16Json = CountedUtf16String + (EventFieldFormat.Json << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 16-bit bytecount followed by Char8 data. + /// Decoding treats as MBCS JSON. + /// </summary> + CountedMbcsJson = CountedMbcsString + (EventFieldFormat.Json << 8), +#if false + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as Win32 error. + /// </summary> + Win32Error = UInt32 + (EventSourceFieldFormat.Win32Error << 8), + + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as NTSTATUS. + /// </summary> + NTStatus = UInt32 + (EventSourceFieldFormat.NTStatus << 8), +#endif + /// <summary> + /// Formatted type. + /// Encoding assumes 32-bit value. + /// Decoding treats as HRESULT. + /// </summary> + HResult = Int32 + (EventFieldFormat.HResult << 8) + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventSource.cs new file mode 100644 index 0000000000..7eea8dfaca --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventSource.cs @@ -0,0 +1,842 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. +// It is available from http://www.codeplex.com/hyperAddin +#define FEATURE_MANAGED_ETW + +#if !ES_BUILD_STANDALONE +#define FEATURE_ACTIVITYSAMPLING +#endif + +#if ES_BUILD_STANDALONE +#define FEATURE_MANAGED_ETW_CHANNELS +// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS +#endif + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +#endif + +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Collections.ObjectModel; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +using System.Collections.Generic; +using System.Text; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +using System.Collections.Generic; +using System.Text; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + public partial class EventSource + { + private byte[] providerMetadata; + + /// <summary> + /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). + /// </summary> + /// <param name="eventSourceName"> + /// The name of the event source. Must not be null. + /// </param> + public EventSource( + string eventSourceName) + : this(eventSourceName, EventSourceSettings.EtwSelfDescribingEventFormat) + { } + + /// <summary> + /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). + /// </summary> + /// <param name="eventSourceName"> + /// The name of the event source. Must not be null. + /// </param> + /// <param name="config"> + /// Configuration options for the EventSource as a whole. + /// </param> + public EventSource( + string eventSourceName, + EventSourceSettings config) + : this(eventSourceName, config, null) { } + + /// <summary> + /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API). + /// + /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). + /// The first string is the key and the second is the value. These are not interpreted by EventSource + /// itself but may be interprated the listeners. Can be fetched with GetTrait(string). + /// </summary> + /// <param name="eventSourceName"> + /// The name of the event source. Must not be null. + /// </param> + /// <param name="config"> + /// Configuration options for the EventSource as a whole. + /// </param> + /// <param name="traits">A collection of key-value strings (must be an even number).</param> + public EventSource( + string eventSourceName, + EventSourceSettings config, + params string[] traits) + : this( + eventSourceName == null ? new Guid() : GenerateGuidFromName(eventSourceName.ToUpperInvariant()), + eventSourceName, + config, traits) + { + if (eventSourceName == null) + { + throw new ArgumentNullException("eventSourceName"); + } + Contract.EndContractBlock(); + } + + /// <summary> + /// Writes an event with no fields and default options. + /// (Native API: EventWriteTransfer) + /// </summary> + /// <param name="eventName">The name of the event. Must not be null.</param> + [SecuritySafeCritical] + public unsafe void Write(string eventName) + { + if (eventName == null) + { + throw new ArgumentNullException("eventName"); + } + + Contract.EndContractBlock(); + + if (!this.IsEnabled()) + { + return; + } + + var options = new EventSourceOptions(); + var data = new EmptyStruct(); + this.WriteImpl(eventName, ref options, ref data, null, null); + } + + /// <summary> + /// Writes an event with no fields. + /// (Native API: EventWriteTransfer) + /// </summary> + /// <param name="eventName">The name of the event. Must not be null.</param> + /// <param name="options"> + /// Options for the event, such as the level, keywords, and opcode. Unset + /// options will be set to default values. + /// </param> + [SecuritySafeCritical] + public unsafe void Write(string eventName, EventSourceOptions options) + { + if (eventName == null) + { + throw new ArgumentNullException("eventName"); + } + + Contract.EndContractBlock(); + + if (!this.IsEnabled()) + { + return; + } + + var data = new EmptyStruct(); + this.WriteImpl(eventName, ref options, ref data, null, null); + } + + /// <summary> + /// Writes an event. + /// (Native API: EventWriteTransfer) + /// </summary> + /// <typeparam name="T"> + /// The type that defines the event and its payload. This must be an + /// anonymous type or a type with an [EventData] attribute. + /// </typeparam> + /// <param name="eventName"> + /// The name for the event. If null, the event name is automatically + /// determined based on T, either from the Name property of T's EventData + /// attribute or from typeof(T).Name. + /// </param> + /// <param name="data"> + /// The object containing the event payload data. The type T must be + /// an anonymous type or a type with an [EventData] attribute. The + /// public instance properties of data will be written recursively to + /// create the fields of the event. + /// </param> + [SecuritySafeCritical] + public unsafe void Write<T>( + string eventName, + T data) + { + if (!this.IsEnabled()) + { + return; + } + + var options = new EventSourceOptions(); + this.WriteImpl(eventName, ref options, ref data, null, null); + } + + /// <summary> + /// Writes an event. + /// (Native API: EventWriteTransfer) + /// </summary> + /// <typeparam name="T"> + /// The type that defines the event and its payload. This must be an + /// anonymous type or a type with an [EventData] attribute. + /// </typeparam> + /// <param name="eventName"> + /// The name for the event. If null, the event name is automatically + /// determined based on T, either from the Name property of T's EventData + /// attribute or from typeof(T).Name. + /// </param> + /// <param name="options"> + /// Options for the event, such as the level, keywords, and opcode. Unset + /// options will be set to default values. + /// </param> + /// <param name="data"> + /// The object containing the event payload data. The type T must be + /// an anonymous type or a type with an [EventData] attribute. The + /// public instance properties of data will be written recursively to + /// create the fields of the event. + /// </param> + [SecuritySafeCritical] + public unsafe void Write<T>( + string eventName, + EventSourceOptions options, + T data) + { + if (!this.IsEnabled()) + { + return; + } + + this.WriteImpl(eventName, ref options, ref data, null, null); + } + + /// <summary> + /// Writes an event. + /// This overload is for use with extension methods that wish to efficiently + /// forward the options or data parameter without performing an extra copy. + /// (Native API: EventWriteTransfer) + /// </summary> + /// <typeparam name="T"> + /// The type that defines the event and its payload. This must be an + /// anonymous type or a type with an [EventData] attribute. + /// </typeparam> + /// <param name="eventName"> + /// The name for the event. If null, the event name is automatically + /// determined based on T, either from the Name property of T's EventData + /// attribute or from typeof(T).Name. + /// </param> + /// <param name="options"> + /// Options for the event, such as the level, keywords, and opcode. Unset + /// options will be set to default values. + /// </param> + /// <param name="data"> + /// The object containing the event payload data. The type T must be + /// an anonymous type or a type with an [EventData] attribute. The + /// public instance properties of data will be written recursively to + /// create the fields of the event. + /// </param> + [SecuritySafeCritical] + public unsafe void Write<T>( + string eventName, + ref EventSourceOptions options, + ref T data) + { + if (!this.IsEnabled()) + { + return; + } + + this.WriteImpl(eventName, ref options, ref data, null, null); + } + + /// <summary> + /// Writes an event. + /// This overload is meant for clients that need to manipuate the activityId + /// and related ActivityId for the event. + /// </summary> + /// <typeparam name="T"> + /// The type that defines the event and its payload. This must be an + /// anonymous type or a type with an [EventData] attribute. + /// </typeparam> + /// <param name="eventName"> + /// The name for the event. If null, the event name is automatically + /// determined based on T, either from the Name property of T's EventData + /// attribute or from typeof(T).Name. + /// </param> + /// <param name="options"> + /// Options for the event, such as the level, keywords, and opcode. Unset + /// options will be set to default values. + /// </param> + /// <param name="activityId"> + /// The GUID of the activity associated with this event. + /// </param> + /// <param name="relatedActivityId"> + /// The GUID of another activity that is related to this activity, or Guid.Empty + /// if there is no related activity. Most commonly, the Start operation of a + /// new activity specifies a parent activity as its related activity. + /// </param> + /// <param name="data"> + /// The object containing the event payload data. The type T must be + /// an anonymous type or a type with an [EventData] attribute. The + /// public instance properties of data will be written recursively to + /// create the fields of the event. + /// </param> + [SecuritySafeCritical] + public unsafe void Write<T>( + string eventName, + ref EventSourceOptions options, + ref Guid activityId, + ref Guid relatedActivityId, + ref T data) + { + if (!this.IsEnabled()) + { + return; + } + + fixed (Guid* pActivity = &activityId, pRelated = &relatedActivityId) + { + this.WriteImpl( + eventName, + ref options, + ref data, + pActivity, + relatedActivityId == Guid.Empty ? null : pRelated); + } + } + + /// <summary> + /// Writes an extended event, where the values of the event are the + /// combined properties of any number of values. This method is + /// intended for use in advanced logging scenarios that support a + /// dynamic set of event context providers. + /// This method does a quick check on whether this event is enabled. + /// </summary> + /// <param name="eventName"> + /// The name for the event. If null, the name from eventTypes is used. + /// (Note that providing the event name via the name parameter is slightly + /// less efficient than using the name from eventTypes.) + /// </param> + /// <param name="options"> + /// Optional overrides for the event, such as the level, keyword, opcode, + /// activityId, and relatedActivityId. Any settings not specified by options + /// are obtained from eventTypes. + /// </param> + /// <param name="eventTypes"> + /// Information about the event and the types of the values in the event. + /// Must not be null. Note that the eventTypes object should be created once and + /// saved. It should not be recreated for each event. + /// </param> + /// <param name="values"> + /// The values to include in the event. Must not be null. The number and types of + /// the values must match the number and types of the fields described by the + /// eventTypes parameter. + /// </param> + [SecuritySafeCritical] + private unsafe void WriteMultiMerge( + string eventName, + ref EventSourceOptions options, + TraceLoggingEventTypes eventTypes, + Guid* activityID, + Guid* childActivityID, + params object[] values) + { + if (!this.IsEnabled()) + { + return; + } + byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 + ? options.level + : eventTypes.level; + EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 + ? options.keywords + : eventTypes.keywords; + + if (this.IsEnabled((EventLevel)level, keywords)) + { + WriteMultiMergeInner(eventName, ref options, eventTypes, activityID, childActivityID, values); + } + } + + /// <summary> + /// Writes an extended event, where the values of the event are the + /// combined properties of any number of values. This method is + /// intended for use in advanced logging scenarios that support a + /// dynamic set of event context providers. + /// Attention: This API does not check whether the event is enabled or not. + /// Please use WriteMultiMerge to avoid spending CPU cycles for events that are + /// not enabled. + /// </summary> + /// <param name="eventName"> + /// The name for the event. If null, the name from eventTypes is used. + /// (Note that providing the event name via the name parameter is slightly + /// less efficient than using the name from eventTypes.) + /// </param> + /// <param name="options"> + /// Optional overrides for the event, such as the level, keyword, opcode, + /// activityId, and relatedActivityId. Any settings not specified by options + /// are obtained from eventTypes. + /// </param> + /// <param name="eventTypes"> + /// Information about the event and the types of the values in the event. + /// Must not be null. Note that the eventTypes object should be created once and + /// saved. It should not be recreated for each event. + /// </param> + /// <param name="values"> + /// The values to include in the event. Must not be null. The number and types of + /// the values must match the number and types of the fields described by the + /// eventTypes parameter. + /// </param> + [SecuritySafeCritical] + private unsafe void WriteMultiMergeInner( + string eventName, + ref EventSourceOptions options, + TraceLoggingEventTypes eventTypes, + Guid* activityID, + Guid* childActivityID, + params object[] values) + { + int identity = 0; + byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 + ? options.level + : eventTypes.level; + byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0 + ? options.opcode + : eventTypes.opcode; + EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0 + ? options.tags + : eventTypes.Tags; + EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 + ? options.keywords + : eventTypes.keywords; + + var nameInfo = eventTypes.GetNameInfo(eventName ?? eventTypes.Name, tags); + if (nameInfo == null) + { + return; + } + identity = nameInfo.identity; + EventDescriptor descriptor = new EventDescriptor(identity, level, opcode, (long)keywords); + + var pinCount = eventTypes.pinCount; + var scratch = stackalloc byte[eventTypes.scratchSize]; + var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + + fixed (byte* + pMetadata0 = this.providerMetadata, + pMetadata1 = nameInfo.nameMetadata, + pMetadata2 = eventTypes.typeMetadata) + { + descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); + descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); + +#if !ES_BUILD_PCL + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); +#endif + try + { + DataCollector.ThreadInstance.Enable( + scratch, + eventTypes.scratchSize, + descriptors + 3, + eventTypes.dataCount, + pins, + pinCount); + + for (int i = 0; i < eventTypes.typeInfos.Length; i++) + { + eventTypes.typeInfos[i].WriteObjectData(TraceLoggingDataCollector.Instance, values[i]); + } + + this.WriteEventRaw( + ref descriptor, + activityID, + childActivityID, + (int)(DataCollector.ThreadInstance.Finish() - descriptors), + (IntPtr)descriptors); + } + finally + { + this.WriteCleanup(pins, pinCount); + } + } + + } + + /// <summary> + /// Writes an extended event, where the values of the event have already + /// been serialized in "data". + /// </summary> + /// <param name="eventName"> + /// The name for the event. If null, the name from eventTypes is used. + /// (Note that providing the event name via the name parameter is slightly + /// less efficient than using the name from eventTypes.) + /// </param> + /// <param name="options"> + /// Optional overrides for the event, such as the level, keyword, opcode, + /// activityId, and relatedActivityId. Any settings not specified by options + /// are obtained from eventTypes. + /// </param> + /// <param name="eventTypes"> + /// Information about the event and the types of the values in the event. + /// Must not be null. Note that the eventTypes object should be created once and + /// saved. It should not be recreated for each event. + /// </param> + /// <param name="data"> + /// The previously serialized values to include in the event. Must not be null. + /// The number and types of the values must match the number and types of the + /// fields described by the eventTypes parameter. + /// </param> + [SecuritySafeCritical] + internal unsafe void WriteMultiMerge( + string eventName, + ref EventSourceOptions options, + TraceLoggingEventTypes eventTypes, + Guid* activityID, + Guid* childActivityID, + EventData* data) + { + if (!this.IsEnabled()) + { + return; + } + + fixed (EventSourceOptions* pOptions = &options) + { + EventDescriptor descriptor; + var nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor); + if (nameInfo == null) + { + return; + } + + // 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 descriptors = stackalloc EventData[eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3]; + + fixed (byte* + pMetadata0 = this.providerMetadata, + pMetadata1 = nameInfo.nameMetadata, + pMetadata2 = eventTypes.typeMetadata) + { + descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); + descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); + int numDescrs = 3; + + for (int i = 0; i < eventTypes.typeInfos.Length; i++) + { + // Until M3, we need to morph strings to a counted representation + // When TDH supports null terminated strings, we can remove this. + if (eventTypes.typeInfos[i].DataType == typeof(string)) + { + // Write out the size of the string + descriptors[numDescrs].m_Ptr = (long)&descriptors[numDescrs + 1].m_Size; + descriptors[numDescrs].m_Size = 2; + numDescrs++; + + descriptors[numDescrs].m_Ptr = data[i].m_Ptr; + descriptors[numDescrs].m_Size = data[i].m_Size - 2; // Remove the null terminator + numDescrs++; + } + else + { + descriptors[numDescrs].m_Ptr = data[i].m_Ptr; + descriptors[numDescrs].m_Size = data[i].m_Size; + + // old conventions for bool is 4 bytes, but meta-data assumes 1. + if (data[i].m_Size == 4 && eventTypes.typeInfos[i].DataType == typeof(bool)) + descriptors[numDescrs].m_Size = 1; + + numDescrs++; + } + } + + this.WriteEventRaw( + ref descriptor, + activityID, + childActivityID, + numDescrs, + (IntPtr)descriptors); + } + } + } + + [SecuritySafeCritical] + private unsafe void WriteImpl<T>( + string eventName, + ref EventSourceOptions options, + ref T data, + Guid* pActivityId, + Guid* pRelatedActivityId) + { + try + { + var eventTypes = SimpleEventTypes<T>.Instance; + + fixed (EventSourceOptions* pOptions = &options) + { + EventDescriptor descriptor; + options.Opcode = options.IsOpcodeSet ? options.Opcode : GetOpcodeWithDefault(options.Opcode, eventName); + var nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor); + if (nameInfo == null) + { + return; + } + + var pinCount = eventTypes.pinCount; + var scratch = stackalloc byte[eventTypes.scratchSize]; + var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + + fixed (byte* + pMetadata0 = this.providerMetadata, + pMetadata1 = nameInfo.nameMetadata, + pMetadata2 = eventTypes.typeMetadata) + { + descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); + descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); + +#if !ES_BUILD_PCL + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); +#endif + EventOpcode opcode = (EventOpcode)descriptor.Opcode; + + Guid activityId = Guid.Empty; + Guid relatedActivityId = Guid.Empty; + if (pActivityId == null && pRelatedActivityId == null && + ((options.ActivityOptions & EventActivityOptions.Disable) == 0)) + { + if (opcode == EventOpcode.Start) + { + m_activityTracker.OnStart(m_name, eventName, 0, ref activityId, ref relatedActivityId, options.ActivityOptions); + } + else if (opcode == EventOpcode.Stop) + { + m_activityTracker.OnStop(m_name, eventName, 0, ref activityId); + } + if (activityId != Guid.Empty) + pActivityId = &activityId; + if (relatedActivityId != Guid.Empty) + pRelatedActivityId = &relatedActivityId; + } + + try + { + DataCollector.ThreadInstance.Enable( + scratch, + eventTypes.scratchSize, + descriptors + 3, + eventTypes.dataCount, + pins, + pinCount); + + eventTypes.typeInfo.WriteData(TraceLoggingDataCollector.Instance, ref data); + + this.WriteEventRaw( + ref descriptor, + pActivityId, + pRelatedActivityId, + (int)(DataCollector.ThreadInstance.Finish() - descriptors), + (IntPtr)descriptors); + + // TODO enable filtering for listners. + if (m_Dispatchers != null) + { + var eventData = (EventPayload)(eventTypes.typeInfo.GetData(data)); + WriteToAllListeners(eventName, pActivityId, eventData); + } + + } + catch(Exception ex) + { + if (ex is EventSourceException) + throw; + else + ThrowEventSourceException(ex); + } + finally + { + this.WriteCleanup(pins, pinCount); + } + } + } + } + catch (Exception ex) + { + if (ex is EventSourceException) + throw; + else + ThrowEventSourceException(ex); + } + } + + [SecurityCritical] + private unsafe void WriteToAllListeners(string eventName, Guid* pActivityId, EventPayload payload) + { + EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this); + eventCallbackArgs.EventName = eventName; + + // Self described events do not have an id attached. We mark it internally with -1. + eventCallbackArgs.EventId = -1; + if (pActivityId != null) + eventCallbackArgs.RelatedActivityId = *pActivityId; + + if (payload != null) + { + eventCallbackArgs.Payload = new ReadOnlyCollection<object>((IList<object>)payload.Values); + eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>((IList<string>)payload.Keys); + } + + DisptachToAllListeners(-1, pActivityId, eventCallbackArgs); + } + +#if !ES_BUILD_PCL + [System.Runtime.ConstrainedExecution.ReliabilityContract( + System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, + System.Runtime.ConstrainedExecution.Cer.Success)] +#endif + [SecurityCritical] + [NonEvent] + private unsafe void WriteCleanup(GCHandle* pPins, int cPins) + { + DataCollector.ThreadInstance.Disable(); + + for (int i = 0; i != cPins; i++) + { + if (IntPtr.Zero != (IntPtr)pPins[i]) + { + pPins[i].Free(); + } + } + } + + private void InitializeProviderMetadata() + { + if (m_traits != null) + { + List<byte> traitMetaData = new List<byte>(100); + for (int i = 0; i < m_traits.Length - 1; i += 2) + { + if (m_traits[i].StartsWith("ETW_")) + { + string etwTrait = m_traits[i].Substring(4); + byte traitNum; + if (!byte.TryParse(etwTrait, out traitNum)) + { + if (etwTrait == "GROUP") + traitNum = 1; + else + throw new ArgumentException(Environment.GetResourceString("UnknownEtwTrait", etwTrait), "traits"); + } + string value = m_traits[i + 1]; + int lenPos = traitMetaData.Count; + traitMetaData.Add(0); // Emit size (to be filled in later) + traitMetaData.Add(0); + traitMetaData.Add(traitNum); // Emit Trait number + var valueLen = AddValueToMetaData(traitMetaData, value) + 3; // Emit the value bytes +3 accounts for 3 bytes we emited above. + traitMetaData[lenPos] = unchecked((byte)valueLen); // Fill in size + traitMetaData[lenPos + 1] = unchecked((byte)(valueLen >> 8)); + } + } + providerMetadata = Statics.MetadataForString(this.Name, 0, traitMetaData.Count, 0); + int startPos = providerMetadata.Length-traitMetaData.Count; + foreach (var b in traitMetaData) + providerMetadata[startPos++] = b; + } + else + providerMetadata = Statics.MetadataForString(this.Name, 0, 0, 0); + } + + private static int AddValueToMetaData(List<byte> metaData, string value) + { + if (value.Length == 0) + return 0; + + int startPos = metaData.Count; + char firstChar = value[0]; + + if (firstChar == '@') + metaData.AddRange(Encoding.UTF8.GetBytes(value.Substring(1))); + else if (firstChar == '{') + metaData.AddRange(new Guid(value).ToByteArray()); + else if (firstChar == '#') + { + for (int i = 1; i < value.Length; i++) + { + if (value[i] != ' ') // Skp spaces between bytes. + { + if (!(i + 1 < value.Length)) + throw new ArgumentException(Environment.GetResourceString("EvenHexDigits"), "traits"); + metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1]))); + i++; + } + } + } + else if (' ' <= firstChar) // Is it alphabetic (excludes digits and most punctuation. + metaData.AddRange(Encoding.UTF8.GetBytes(value)); + else + throw new ArgumentException(Environment.GetResourceString("IllegalValue", value), "traits"); + + return metaData.Count - startPos; + } + + /// <summary> + /// Returns a value 0-15 if 'c' is a hexadecimal digit. If it throws an argument exception. + /// </summary> + private static int HexDigit(char c) + { + if ('0' <= c && c <= '9') + return (c - '0'); + if ('a' <= c) + c = unchecked((char) (c - ('a' - 'A'))); // Convert to lower case + if ('A' <= c && c <= 'F') + return (c - 'A' + 10); + throw new ArgumentException(Environment.GetResourceString("BadHexDigit", c), "traits"); + } + + private NameInfo UpdateDescriptor( + string name, + TraceLoggingEventTypes eventInfo, + ref EventSourceOptions options, + out EventDescriptor descriptor) + { + NameInfo nameInfo = null; + int identity = 0; + byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0 + ? options.level + : eventInfo.level; + byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0 + ? options.opcode + : eventInfo.opcode; + EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0 + ? options.tags + : eventInfo.Tags; + EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0 + ? options.keywords + : eventInfo.keywords; + + if (this.IsEnabled((EventLevel)level, keywords)) + { + nameInfo = eventInfo.GetNameInfo(name ?? eventInfo.Name, tags); + identity = nameInfo.identity; + } + + descriptor = new EventDescriptor(identity, level, opcode, (long)keywords); + return nameInfo; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTraits.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTraits.cs new file mode 100644 index 0000000000..6c5a5793bb --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTraits.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Tags are flags that are not interpreted by EventSource but are passed along + /// to the EventListener. The EventListener determines the semantics of the flags. + /// </summary> + [Flags] + public enum EventTags + { + /// <summary> + /// No special traits are added to the event. + /// </summary> + None = 0, + + /* Bits below 0x10000 are available for any use by the provider. */ + /* Bits at or above 0x10000 are reserved for definition by Microsoft. */ + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTypes.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTypes.cs new file mode 100644 index 0000000000..8e2732b0ff --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTypes.cs @@ -0,0 +1,260 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using Interlocked = System.Threading.Interlocked; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: Used when calling EventSource.WriteMultiMerge. + /// Stores the type information to use when writing the event fields. + /// </summary> + internal class TraceLoggingEventTypes + { + internal readonly TraceLoggingTypeInfo[] typeInfos; + internal readonly string name; + internal readonly EventTags tags; + internal readonly byte level; + internal readonly byte opcode; + internal readonly EventKeywords keywords; + internal readonly byte[] typeMetadata; + internal readonly int scratchSize; + internal readonly int dataCount; + internal readonly int pinCount; + private ConcurrentSet<KeyValuePair<string, EventTags>, NameInfo> nameInfos; + + /// <summary> + /// Initializes a new instance of TraceLoggingEventTypes corresponding + /// to the name, flags, and types provided. Always uses the default + /// TypeInfo for each Type. + /// </summary> + /// <param name="name"> + /// The name to use when the name parameter passed to + /// EventSource.Write is null. This value must not be null. + /// </param> + /// <param name="tags"> + /// Tags to add to the event if the tags are not set via options. + /// </param> + /// <param name="types"> + /// The types of the fields in the event. This value must not be null. + /// </param> + internal TraceLoggingEventTypes( + string name, + EventTags tags, + params Type[] types) + : this(tags, name, MakeArray(types)) + { + return; + } + + /// <summary> + /// Returns a new instance of TraceLoggingEventInfo corresponding to the name, + /// flags, and typeInfos provided. + /// </summary> + /// <param name="name"> + /// The name to use when the name parameter passed to + /// EventSource.Write is null. This value must not be null. + /// </param> + /// <param name="tags"> + /// Tags to add to the event if the tags are not set via options. + /// </param> + /// <param name="typeInfos"> + /// The types of the fields in the event. This value must not be null. + /// </param> + /// <returns> + /// An instance of TraceLoggingEventInfo with DefaultName set to the specified name + /// and with the specified typeInfos. + /// </returns> + internal TraceLoggingEventTypes( + string name, + EventTags tags, + params TraceLoggingTypeInfo[] typeInfos) + : this(tags, name, MakeArray(typeInfos)) + { + return; + } + + internal TraceLoggingEventTypes( + string name, + EventTags tags, + System.Reflection.ParameterInfo[] paramInfos) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + Contract.EndContractBlock(); + + this.typeInfos = MakeArray(paramInfos); + this.name = name; + this.tags = tags; + this.level = Statics.DefaultLevel; + + var collector = new TraceLoggingMetadataCollector(); + for (int i = 0; i < typeInfos.Length; ++i) + { + var typeInfo = typeInfos[i]; + this.level = Statics.Combine((int)typeInfo.Level, this.level); + this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode); + this.keywords |= typeInfo.Keywords; + var paramName = paramInfos[i].Name; + if (Statics.ShouldOverrideFieldName(paramName)) + { + paramName = typeInfo.Name; + } + typeInfo.WriteMetadata(collector, paramName, EventFieldFormat.Default); + } + + this.typeMetadata = collector.GetMetadata(); + this.scratchSize = collector.ScratchSize; + this.dataCount = collector.DataCount; + this.pinCount = collector.PinCount; + } + + private TraceLoggingEventTypes( + EventTags tags, + string defaultName, + TraceLoggingTypeInfo[] typeInfos) + { + if (defaultName == null) + { + throw new ArgumentNullException("defaultName"); + } + + Contract.EndContractBlock(); + + this.typeInfos = typeInfos; + this.name = defaultName; + this.tags = tags; + this.level = Statics.DefaultLevel; + + var collector = new TraceLoggingMetadataCollector(); + foreach (var typeInfo in typeInfos) + { + this.level = Statics.Combine((int)typeInfo.Level, this.level); + this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode); + this.keywords |= typeInfo.Keywords; + typeInfo.WriteMetadata(collector, null, EventFieldFormat.Default); + } + + this.typeMetadata = collector.GetMetadata(); + this.scratchSize = collector.ScratchSize; + this.dataCount = collector.DataCount; + this.pinCount = collector.PinCount; + } + + /// <summary> + /// Gets the default name that will be used for events with this descriptor. + /// </summary> + internal string Name + { + get { return this.name; } + } + + /// <summary> + /// Gets the default level that will be used for events with this descriptor. + /// </summary> + internal EventLevel Level + { + get { return (EventLevel)this.level; } + } + + /// <summary> + /// Gets the default opcode that will be used for events with this descriptor. + /// </summary> + internal EventOpcode Opcode + { + get { return (EventOpcode)this.opcode; } + } + + /// <summary> + /// Gets the default set of keywords that will added to events with this descriptor. + /// </summary> + internal EventKeywords Keywords + { + get { return (EventKeywords)this.keywords; } + } + + /// <summary> + /// Gets the default tags that will be added events with this descriptor. + /// </summary> + internal EventTags Tags + { + get { return this.tags; } + } + + internal NameInfo GetNameInfo(string name, EventTags tags) + { + var ret = this.nameInfos.TryGet(new KeyValuePair<string, EventTags>(name, tags)); + if (ret == null) + { + ret = this.nameInfos.GetOrAdd(new NameInfo(name, tags, this.typeMetadata.Length)); + } + + return ret; + } + + private TraceLoggingTypeInfo[] MakeArray(System.Reflection.ParameterInfo[] paramInfos) + { + if (paramInfos == null) + { + throw new ArgumentNullException("paramInfos"); + } + + Contract.EndContractBlock(); + + var recursionCheck = new List<Type>(paramInfos.Length); + var result = new TraceLoggingTypeInfo[paramInfos.Length]; + for (int i = 0; i < paramInfos.Length; ++i) + { + result[i] = Statics.GetTypeInfoInstance(paramInfos[i].ParameterType, recursionCheck); + } + + return result; + } + + private static TraceLoggingTypeInfo[] MakeArray(Type[] types) + { + if (types == null) + { + throw new ArgumentNullException("types"); + } + + Contract.EndContractBlock(); + + var recursionCheck = new List<Type>(types.Length); + var result = new TraceLoggingTypeInfo[types.Length]; + for (int i = 0; i < types.Length; i++) + { + result[i] = Statics.GetTypeInfoInstance(types[i], recursionCheck); + } + + return result; + } + + private static TraceLoggingTypeInfo[] MakeArray( + TraceLoggingTypeInfo[] typeInfos) + { + if (typeInfos == null) + { + throw new ArgumentNullException("typeInfos"); + } + + Contract.EndContractBlock(); + + return (TraceLoggingTypeInfo[])typeInfos.Clone(); ; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingMetadataCollector.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingMetadataCollector.cs new file mode 100644 index 0000000000..1101439d66 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingMetadataCollector.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. + /// An instance of this type is provided to the TypeInfo.WriteMetadata method. + /// </summary> + internal class TraceLoggingMetadataCollector + { + private readonly Impl impl; + private readonly FieldMetadata currentGroup; + private int bufferedArrayFieldCount = int.MinValue; + + /// <summary> + /// Creates a root-level collector. + /// </summary> + internal TraceLoggingMetadataCollector() + { + this.impl = new Impl(); + } + + /// <summary> + /// Creates a collector for a group. + /// </summary> + /// <param name="other">Parent collector</param> + /// <param name="group">The field that starts the group</param> + private TraceLoggingMetadataCollector( + TraceLoggingMetadataCollector other, + FieldMetadata group) + { + this.impl = other.impl; + this.currentGroup = group; + } + + /// <summary> + /// The field tags to be used for the next field. + /// This will be reset to None each time a field is written. + /// </summary> + internal EventFieldTags Tags + { + get; + set; + } + + internal int ScratchSize + { + get { return this.impl.scratchSize; } + } + + internal int DataCount + { + get { return this.impl.dataCount; } + } + + internal int PinCount + { + get { return this.impl.pinCount; } + } + + private bool BeginningBufferedArray + { + get { return this.bufferedArrayFieldCount == 0; } + } + + /// <summary> + /// Call this method to add a group to the event and to return + /// a new metadata collector that can be used to add fields to the + /// group. After all of the fields in the group have been written, + /// switch back to the original metadata collector to add fields + /// outside of the group. + /// Special-case: if name is null, no group is created, and AddGroup + /// returns the original metadata collector. This is useful when + /// adding the top-level group for an event. + /// Note: do not use the original metadata collector while the group's + /// metadata collector is in use, and do not use the group's metadata + /// collector after switching back to the original. + /// </summary> + /// <param name="name"> + /// The name of the group. If name is null, the call to AddGroup is a + /// no-op (collector.AddGroup(null) returns collector). + /// </param> + /// <returns> + /// A new metadata collector that can be used to add fields to the group. + /// </returns> + public TraceLoggingMetadataCollector AddGroup(string name) + { + TraceLoggingMetadataCollector result = this; + + if (name != null || // Normal. + this.BeginningBufferedArray) // Error, FieldMetadata's constructor will throw the appropriate exception. + { + var newGroup = new FieldMetadata( + name, + TraceLoggingDataType.Struct, + 0, + this.BeginningBufferedArray); + this.AddField(newGroup); + result = new TraceLoggingMetadataCollector(this, newGroup); + } + + return result; + } + + /// <summary> + /// Adds a scalar field to an event. + /// </summary> + /// <param name="name"> + /// The name to use for the added field. This value must not be null. + /// </param> + /// <param name="type"> + /// The type code for the added field. This must be a fixed-size type + /// (e.g. string types are not supported). + /// </param> + public void AddScalar(string name, TraceLoggingDataType type) + { + int size; + switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) + { + case TraceLoggingDataType.Int8: + case TraceLoggingDataType.UInt8: + case TraceLoggingDataType.Char8: + size = 1; + break; + case TraceLoggingDataType.Int16: + case TraceLoggingDataType.UInt16: + case TraceLoggingDataType.Char16: + size = 2; + break; + case TraceLoggingDataType.Int32: + case TraceLoggingDataType.UInt32: + case TraceLoggingDataType.HexInt32: + case TraceLoggingDataType.Float: + case TraceLoggingDataType.Boolean32: + size = 4; + break; + case TraceLoggingDataType.Int64: + case TraceLoggingDataType.UInt64: + case TraceLoggingDataType.HexInt64: + case TraceLoggingDataType.Double: + case TraceLoggingDataType.FileTime: + size = 8; + break; + case TraceLoggingDataType.Guid: + case TraceLoggingDataType.SystemTime: + size = 16; + break; + default: + throw new ArgumentOutOfRangeException("type"); + } + + this.impl.AddScalar(size); + this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray)); + } + + /// <summary> + /// Adds a binary-format field to an event. + /// Compatible with core types: Binary, CountedUtf16String, CountedMbcsString. + /// Compatible with dataCollector methods: AddBinary(string), AddArray(Any8bitType[]). + /// </summary> + /// <param name="name"> + /// The name to use for the added field. This value must not be null. + /// </param> + /// <param name="type"> + /// The type code for the added field. This must be a Binary or CountedString type. + /// </param> + public void AddBinary(string name, TraceLoggingDataType type) + { + switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) + { + case TraceLoggingDataType.Binary: + case TraceLoggingDataType.CountedMbcsString: + case TraceLoggingDataType.CountedUtf16String: + break; + default: + throw new ArgumentOutOfRangeException("type"); + } + + this.impl.AddScalar(2); + this.impl.AddNonscalar(); + this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray)); + } + + /// <summary> + /// Adds an array field to an event. + /// </summary> + /// <param name="name"> + /// The name to use for the added field. This value must not be null. + /// </param> + /// <param name="type"> + /// The type code for the added field. This must be a fixed-size type + /// or a string type. In the case of a string type, this adds an array + /// of characters, not an array of strings. + /// </param> + public void AddArray(string name, TraceLoggingDataType type) + { + switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask)) + { + case TraceLoggingDataType.Utf16String: + case TraceLoggingDataType.MbcsString: + case TraceLoggingDataType.Int8: + case TraceLoggingDataType.UInt8: + case TraceLoggingDataType.Int16: + case TraceLoggingDataType.UInt16: + case TraceLoggingDataType.Int32: + case TraceLoggingDataType.UInt32: + case TraceLoggingDataType.Int64: + case TraceLoggingDataType.UInt64: + case TraceLoggingDataType.Float: + case TraceLoggingDataType.Double: + case TraceLoggingDataType.Boolean32: + case TraceLoggingDataType.Guid: + case TraceLoggingDataType.FileTime: + case TraceLoggingDataType.HexInt32: + case TraceLoggingDataType.HexInt64: + case TraceLoggingDataType.Char16: + case TraceLoggingDataType.Char8: + break; + default: + throw new ArgumentOutOfRangeException("type"); + } + + if (this.BeginningBufferedArray) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + } + + this.impl.AddScalar(2); + this.impl.AddNonscalar(); + this.AddField(new FieldMetadata(name, type, this.Tags, true)); + } + + public void BeginBufferedArray() + { + if (this.bufferedArrayFieldCount >= 0) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + } + + this.bufferedArrayFieldCount = 0; + this.impl.BeginBuffered(); + } + + public void EndBufferedArray() + { + if (this.bufferedArrayFieldCount != 1) + { + throw new InvalidOperationException(Environment.GetResourceString("EventSource_IncorrentlyAuthoredTypeInfo")); + } + + this.bufferedArrayFieldCount = int.MinValue; + this.impl.EndBuffered(); + } + + /// <summary> + /// Adds a custom-serialized field to an event. + /// </summary> + /// <param name="name"> + /// The name to use for the added field. This value must not be null. + /// </param> + /// <param name="type">The encoding type for the field.</param> + /// <param name="metadata">Additional information needed to decode the field, if any.</param> + public void AddCustom(string name, TraceLoggingDataType type, byte[] metadata) + { + if (this.BeginningBufferedArray) + { + throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedCustomSerializedData")); + } + + this.impl.AddScalar(2); + this.impl.AddNonscalar(); + this.AddField(new FieldMetadata( + name, + type, + this.Tags, + metadata)); + } + + internal byte[] GetMetadata() + { + var size = this.impl.Encode(null); + var metadata = new byte[size]; + this.impl.Encode(metadata); + return metadata; + } + + private void AddField(FieldMetadata fieldMetadata) + { + this.Tags = EventFieldTags.None; + this.bufferedArrayFieldCount++; + this.impl.fields.Add(fieldMetadata); + + if (this.currentGroup != null) + { + this.currentGroup.IncrementStructFieldCount(); + } + } + + private class Impl + { + internal readonly List<FieldMetadata> fields = new List<FieldMetadata>(); + internal short scratchSize; + internal sbyte dataCount; + internal sbyte pinCount; + private int bufferNesting; + private bool scalar; + + public void AddScalar(int size) + { + if (this.bufferNesting == 0) + { + if (!this.scalar) + { + this.dataCount = checked((sbyte)(this.dataCount + 1)); + } + + this.scalar = true; + this.scratchSize = checked((short)(this.scratchSize + size)); + } + } + + public void AddNonscalar() + { + if (this.bufferNesting == 0) + { + this.scalar = false; + this.pinCount = checked((sbyte)(this.pinCount + 1)); + this.dataCount = checked((sbyte)(this.dataCount + 1)); + } + } + + public void BeginBuffered() + { + if (this.bufferNesting == 0) + { + this.AddNonscalar(); + } + + this.bufferNesting++; + } + + public void EndBuffered() + { + this.bufferNesting--; + } + + public int Encode(byte[] metadata) + { + int size = 0; + + foreach (var field in this.fields) + { + field.Encode(ref size, metadata); + } + + return size; + } + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo.cs new file mode 100644 index 0000000000..326af51c10 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. + /// Non-generic base class for TraceLoggingTypeInfo<DataType>. Do not derive + /// from this class. Instead, derive from TraceLoggingTypeInfo<DataType>. + /// </summary> + internal abstract class TraceLoggingTypeInfo + { + private readonly string name; + private readonly EventKeywords keywords; + private readonly EventLevel level = (EventLevel)(-1); + private readonly EventOpcode opcode = (EventOpcode)(-1); + private readonly EventTags tags; + private readonly Type dataType; + + internal TraceLoggingTypeInfo(Type dataType) + { + if (dataType == null) + { + throw new ArgumentNullException("dataType"); + } + + Contract.EndContractBlock(); + + this.name = dataType.Name; + this.dataType = dataType; + } + + internal TraceLoggingTypeInfo( + Type dataType, + string name, + EventLevel level, + EventOpcode opcode, + EventKeywords keywords, + EventTags tags) + { + if (dataType == null) + { + throw new ArgumentNullException("dataType"); + } + + if (name == null) + { + throw new ArgumentNullException("eventName"); + } + + Contract.EndContractBlock(); + + Statics.CheckName(name); + + this.name = name; + this.keywords = keywords; + this.level = level; + this.opcode = opcode; + this.tags = tags; + this.dataType = dataType; + } + + /// <summary> + /// Gets the name to use for the event if this type is the top-level type, + /// or the name to use for an implicitly-named field. + /// Never null. + /// </summary> + public string Name + { + get { return this.name; } + } + + /// <summary> + /// Gets the event level associated with this type. Any value in the range 0..255 + /// is an associated event level. Any value outside the range 0..255 is invalid and + /// indicates that this type has no associated event level. + /// </summary> + public EventLevel Level + { + get { return this.level; } + } + + /// <summary> + /// Gets the event opcode associated with this type. Any value in the range 0..255 + /// is an associated event opcode. Any value outside the range 0..255 is invalid and + /// indicates that this type has no associated event opcode. + /// </summary> + public EventOpcode Opcode + { + get { return this.opcode; } + } + + /// <summary> + /// Gets the keyword(s) associated with this type. + /// </summary> + public EventKeywords Keywords + { + get { return this.keywords; } + } + + /// <summary> + /// Gets the event tags associated with this type. + /// </summary> + public EventTags Tags + { + get { return this.tags; } + } + + internal Type DataType + { + get { return this.dataType; } + } + + /// <summary> + /// When overridden by a derived class, writes the metadata (schema) for + /// this type. Note that the sequence of operations in WriteMetadata should be + /// essentially identical to the sequence of operations in + /// WriteData/WriteObjectData. Otherwise, the metadata and data will not match, + /// which may cause trouble when decoding the event. + /// </summary> + /// <param name="collector"> + /// The object that collects metadata for this object's type. Metadata is written + /// by calling methods on the collector object. Note that if the type contains + /// sub-objects, the implementation of this method may need to call the + /// WriteMetadata method for the type of the sub-object, e.g. by calling + /// TraceLoggingTypeInfo<SubType>.Instance.WriteMetadata(...). + /// </param> + /// <param name="name"> + /// The name of the property that contains an object of this type, or null if this + /// object is being written as a top-level object of an event. Typical usage + /// is to pass this value to collector.AddGroup. + /// </param> + /// <param name="format"> + /// The format attribute for the field that contains an object of this type. + /// </param> + public abstract void WriteMetadata( + TraceLoggingMetadataCollector collector, + string name, + EventFieldFormat format); + + /// <summary> + /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this + /// method. + /// </summary> + /// <param name="collector"> + /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this + /// method. + /// </param> + /// <param name="value"> + /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this + /// method. + /// </param> + public abstract void WriteObjectData( + TraceLoggingDataCollector collector, + object value); + + /// <summary> + /// Fetches the event parameter data for internal serialization. + /// </summary> + /// <param name="value"></param> + /// <returns></returns> + public virtual object GetData(object value) + { + return value; + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo_T.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo_T.cs new file mode 100644 index 0000000000..b93aab9d65 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo_T.cs @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using Interlocked = System.Threading.Interlocked; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. + /// Implementations of this type provide the behaviors that TraceLogging + /// uses to turn objects into event data. TraceLogging provides default + /// implementations of this type, but custom implementations can be used + /// when the default TraceLogging implementation is insufficient. + /// </summary> + /// <typeparam name="DataType"> + /// The type of object that is handled by this implementation. + /// </typeparam> + internal abstract class TraceLoggingTypeInfo<DataType> + : TraceLoggingTypeInfo + { + private static TraceLoggingTypeInfo<DataType> instance; + + /// <summary> + /// Initializes a new instance of the TraceLoggingTypeInfo class with + /// default settings. Uses typeof(DataType).Name for EventName and FieldName. + /// Marks Level and Opcode as unset. Sets Keywords and Traits to 0. + /// </summary> + protected TraceLoggingTypeInfo() + : base(typeof(DataType)) + { + return; + } + + /// <summary> + /// Initializes a new instance of the TraceLoggingTypeInfo class, using + /// the specified values for the EventName, Level, Opcode, Keywords, + /// FieldName, and Traits properties. + /// </summary> + /// <param name="name"> + /// The value for the Name property. Must not contain '\0' characters. + /// Must not be null. + /// </param> + /// <param name="level"> + /// The value for the Level property, or -1 to mark Level as unset. + /// </param> + /// <param name="opcode"> + /// The value for the Opcode property, or -1 to mark Opcode as unset. + /// </param> + /// <param name="keywords"> + /// The value for the Keywords property. + /// </param> + /// <param name="tags"> + /// The value for the Tags property. + /// </param> + protected TraceLoggingTypeInfo( + string name, + EventLevel level, + EventOpcode opcode, + EventKeywords keywords, + EventTags tags) + : base( + typeof(DataType), + name, + level, + opcode, + keywords, + tags) + { + return; + } + + /// <summary> + /// Gets the type info that will be used for handling instances of + /// DataType. If the instance has not already been set, this will + /// call TrySetInstance(automaticSerializer) to set one, where + /// automaticSerializer is the value returned from CreateDefault(), + /// or a do-nothing serializer if CreateDefault() fails. + /// </summary> + public static TraceLoggingTypeInfo<DataType> Instance + { + get + { + return instance ?? InitInstance(); + } + } + + /// <summary> + /// When overridden by a derived class, writes the data (fields) for an instance + /// of DataType. Note that the sequence of operations in WriteData should be + /// essentially identical to the sequence of operations in WriteMetadata. Otherwise, + /// the metadata and data will not match, which may cause trouble when decoding the + /// event. + /// </summary> + /// <param name="collector"> + /// The object that collects the data for the instance. Data is written by calling + /// methods on the collector object. Note that if the type contains sub-objects, + /// the implementation of this method may need to call the WriteData method + /// for the sub-object, e.g. by calling + /// TraceLoggingTypeInfo<SubType>.Instance.WriteData(...). + /// </param> + /// <param name="value"> + /// The value for which data is to be written. + /// </param> + public abstract void WriteData( + TraceLoggingDataCollector collector, + ref DataType value); + + /// <summary> + /// When overridden in a derived class, writes the data (fields) for an instance + /// of DataType. The default implementation of WriteObjectData calls + /// WriteData(collector, (DataType)value). Normally, you will override WriteData + /// and not WriteObjectData. However, if your implementation of WriteData has to + /// cast the value to object, it may be more efficient to reverse this calling + /// pattern, i.e. to implement WriteObjectData, and then implement WriteData as a + /// call to WriteObjectData. + /// </summary> + /// <param name="collector"> + /// The object that collects the data for the instance. Data is written by calling + /// methods on the collector object. Note that if the type contains sub-objects, + /// the implementation of this method may need to call the WriteData method + /// for the sub-object, e.g. by calling + /// TraceLoggingTypeInfo<SubType>.Instance.WriteData(...). + /// </param> + /// <param name="value"> + /// The value for which data is to be written. Note that this value may be null + /// (even for value types) if the property from which the value was read is + /// missing or null. + /// </param> + public override void WriteObjectData( + TraceLoggingDataCollector collector, + object value) + { + var val = value == null ? default(DataType) : (DataType)value; + this.WriteData(collector, ref val); + } + + internal static TraceLoggingTypeInfo<DataType> GetInstance(List<Type> recursionCheck) + { + if (instance == null) + { + var recursionCheckCount = recursionCheck.Count; + var newInstance = Statics.CreateDefaultTypeInfo<DataType>(recursionCheck); + Interlocked.CompareExchange(ref instance, newInstance, null); + recursionCheck.RemoveRange(recursionCheckCount, recursionCheck.Count - recursionCheckCount); + } + + return instance; + } + + private static TraceLoggingTypeInfo<DataType> InitInstance() + { + return GetInstance(new List<Type>()); + } + } +} diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TypeAnalysis.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TypeAnalysis.cs new file mode 100644 index 0000000000..404fdadc31 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TypeAnalysis.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using System.Reflection; + + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: stores the per-type information obtained by reflecting over a type. + /// </summary> + internal sealed class TypeAnalysis + { + internal readonly PropertyAnalysis[] properties; + internal readonly string name; + internal readonly EventKeywords keywords; + internal readonly EventLevel level = (EventLevel)(-1); + internal readonly EventOpcode opcode = (EventOpcode)(-1); + internal readonly EventTags tags; + + public TypeAnalysis( + Type dataType, + EventDataAttribute eventAttrib, + List<Type> recursionCheck) + { + var propertyInfos = Statics.GetProperties(dataType); + var propertyList = new List<PropertyAnalysis>(); + + foreach (var propertyInfo in propertyInfos) + { + if (Statics.HasCustomAttribute(propertyInfo, typeof(EventIgnoreAttribute))) + { + continue; + } + + if (!propertyInfo.CanRead || + propertyInfo.GetIndexParameters().Length != 0) + { + continue; + } + + MethodInfo getterInfo = Statics.GetGetMethod(propertyInfo); + if (getterInfo == null) + { + continue; + } + + if (getterInfo.IsStatic || !getterInfo.IsPublic) + { + continue; + } + + var propertyType = propertyInfo.PropertyType; + var propertyTypeInfo = Statics.GetTypeInfoInstance(propertyType, recursionCheck); + var fieldAttribute = Statics.GetCustomAttribute<EventFieldAttribute>(propertyInfo); + + string propertyName = + fieldAttribute != null && fieldAttribute.Name != null + ? fieldAttribute.Name + : Statics.ShouldOverrideFieldName(propertyInfo.Name) + ? propertyTypeInfo.Name + : propertyInfo.Name; + propertyList.Add(new PropertyAnalysis( + propertyName, + getterInfo, + propertyTypeInfo, + fieldAttribute)); + } + + this.properties = propertyList.ToArray(); + + foreach (var property in this.properties) + { + var typeInfo = property.typeInfo; + this.level = (EventLevel)Statics.Combine((int)typeInfo.Level, (int)this.level); + this.opcode = (EventOpcode)Statics.Combine((int)typeInfo.Opcode, (int)this.opcode); + this.keywords |= typeInfo.Keywords; + this.tags |= typeInfo.Tags; + } + + if (eventAttrib != null) + { + this.level = (EventLevel)Statics.Combine((int)eventAttrib.Level, (int)this.level); + this.opcode = (EventOpcode)Statics.Combine((int)eventAttrib.Opcode, (int)this.opcode); + this.keywords |= eventAttrib.Keywords; + this.tags |= eventAttrib.Tags; + this.name = eventAttrib.Name; + } + + if (this.name == null) + { + this.name = dataType.Name; + } + } + } +} |