summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging')
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs63
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs127
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs25
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs318
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs17
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs30
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs64
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs146
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs76
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs130
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs25
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs155
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs321
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs130
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs231
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs96
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs79
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs252
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs297
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs727
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs104
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs349
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs890
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs28
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs262
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs370
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs209
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs103
30 files changed, 5702 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs
new file mode 100644
index 0000000000..5771354f67
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ internal sealed class ArrayTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo elementInfo;
+
+ public ArrayTypeInfo(Type type, TraceLoggingTypeInfo elementInfo)
+ : base(type)
+ {
+ 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, PropertyValue value)
+ {
+ var bookmark = collector.BeginBufferedArray();
+
+ var count = 0;
+ Array array = (Array)value.ReferenceValue;
+ if (array != null)
+ {
+ count = array.Length;
+ for (int i = 0; i < array.Length; i++)
+ {
+ this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(array.GetValue(i)));
+ }
+ }
+
+ collector.EndBufferedArray(bookmark, count);
+ }
+
+ public override object GetData(object value)
+ {
+ var array = (Array)value;
+ var serializedArray = new object[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ serializedArray[i] = this.elementInfo.GetData(array.GetValue(i));
+ }
+ return serializedArray;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs
new file mode 100644
index 0000000000..76c01c6c06
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs
@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using 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/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs
new file mode 100644
index 0000000000..558dbf670b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs
new file mode 100644
index 0000000000..27aae820e9
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs
@@ -0,0 +1,318 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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>
+ 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(Resources.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, 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)
+ {
+ /*
+ TODO (perf): consider coalescing adjacent buffered regions into a
+ single buffer, similar to what we're already doing for adjacent
+ scalars. In addition, if a type contains a buffered region adjacent
+ to a blittable array, and the blittable array is small, it would be
+ more efficient to buffer the array instead of pinning it.
+ */
+
+ 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(Resources.GetResourceString("EventSource_PinArrayOutOfRange"));
+ }
+
+ var datasTemp = this.datas;
+ if (this.datasEnd <= datasTemp)
+ {
+ throw new IndexOutOfRangeException(Resources.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(Resources.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/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs
new file mode 100644
index 0000000000..bc7fb8c346
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs
new file mode 100644
index 0000000000..7a23378bb1
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#if EVENTSOURCE_GENERICS
+?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>
+ {
+ public static UnderlyingType Cast<ValueType>(ValueType value)
+ {
+ return (UnderlyingType)(object)value;
+ }
+ }
+
+}
+#endif
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs
new file mode 100644
index 0000000000..74a3fa27b2
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ internal sealed class EnumerableTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo elementInfo;
+
+ public EnumerableTypeInfo(Type type, TraceLoggingTypeInfo elementInfo)
+ : base(type)
+ {
+ 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, PropertyValue value)
+ {
+ var bookmark = collector.BeginBufferedArray();
+
+ var count = 0;
+ IEnumerable enumerable = (IEnumerable)value.ReferenceValue;
+ if (enumerable != null)
+ {
+ foreach (var element in enumerable)
+ {
+ this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(element));
+ count++;
+ }
+ }
+
+ collector.EndBufferedArray(bookmark, count);
+ }
+
+ public override object GetData(object value)
+ {
+ var iterType = (IEnumerable)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/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs
new file mode 100644
index 0000000000..cdedf13c64
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs
@@ -0,0 +1,146 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+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&lt;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/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs
new file mode 100644
index 0000000000..1a298c2851
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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.
+ /// TODO REMOVE
+ /// </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/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs
new file mode 100644
index 0000000000..fd77b07965
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs
@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs
new file mode 100644
index 0000000000..769345f78e
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs
new file mode 100644
index 0000000000..5967ad6ab5
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections;
+using System.Diagnostics;
+
+#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)
+ {
+ Debug.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(nameof(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(nameof(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()
+ {
+ for (int i = 0; i < Keys.Count; i++)
+ {
+ yield return new KeyValuePair<string, object>(this.m_names[i], this.m_values[i]);
+ }
+ }
+
+ 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(nameof(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/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
new file mode 100644
index 0000000000..38c1767462
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
@@ -0,0 +1,321 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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(nameof(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/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs
new file mode 100644
index 0000000000..26305a5708
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs
@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs
new file mode 100644
index 0000000000..309226b84d
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs
@@ -0,0 +1,231 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using 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(
+ nameof(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(Resources.GetResourceString("EventSource_NotSupportedArrayOfNil"));
+ }
+ if (coreType == (int)TraceLoggingDataType.Binary)
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfBinary"));
+ }
+#if !BROKEN_UNTIL_M3
+ if (coreType == (int)TraceLoggingDataType.Utf16String ||
+ coreType == (int)TraceLoggingDataType.MbcsString)
+ {
+ throw new NotSupportedException(Resources.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(Resources.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/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs
new file mode 100644
index 0000000000..3e5997bc9b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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.
+ /// </summary>
+ /// <typeparam name="ContainerType">
+ /// Type from which to read values.
+ /// </typeparam>
+ internal sealed class InvokeTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly PropertyAnalysis[] properties;
+
+ public InvokeTypeInfo(
+ Type type,
+ TypeAnalysis typeAnalysis)
+ : base(
+ type,
+ typeAnalysis.name,
+ typeAnalysis.level,
+ typeAnalysis.opcode,
+ typeAnalysis.keywords,
+ typeAnalysis.tags)
+ {
+ if (typeAnalysis.properties.Length != 0)
+ this.properties = typeAnalysis.properties;
+ }
+
+ 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, PropertyValue value)
+ {
+ if (this.properties != null)
+ {
+ foreach (var property in this.properties)
+ {
+ property.typeInfo.WriteData(collector, property.getter(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 = properties[i].propertyInfo.GetValue(value);
+ membersNames.Add(properties[i].name);
+ memebersValues.Add(properties[i].typeInfo.GetData(propertyValue));
+ }
+ return new EventPayload(membersNames, memebersValues);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs
new file mode 100644
index 0000000000..668043ae68
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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>
+ {
+ /// <summary>
+ /// Insure that eventIds strictly less than 'eventId' will not be
+ /// used by the SelfDescribing events.
+ /// </summary>
+ internal static void ReserveEventIDsBelow(int eventId)
+ {
+ for(;;)
+ {
+ int snapshot = lastIdentity;
+ int newIdentity = (lastIdentity & ~0xFFFFFF) + eventId;
+ newIdentity = Math.Max(newIdentity, snapshot); // Should be redundant. as we only create descriptors once.
+ if (Interlocked.CompareExchange(ref lastIdentity, newIdentity, snapshot) == snapshot)
+ break;
+ }
+ }
+
+ 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/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs
new file mode 100644
index 0000000000..1f07539b52
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Reflection;
+
+#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 PropertyInfo propertyInfo;
+ internal readonly Func<PropertyValue, PropertyValue> getter;
+ internal readonly TraceLoggingTypeInfo typeInfo;
+ internal readonly EventFieldAttribute fieldAttribute;
+
+ public PropertyAnalysis(
+ string name,
+ PropertyInfo propertyInfo,
+ TraceLoggingTypeInfo typeInfo,
+ EventFieldAttribute fieldAttribute)
+ {
+ this.name = name;
+ this.propertyInfo = propertyInfo;
+ this.getter = PropertyValue.GetPropertyGetter(propertyInfo);
+ this.typeInfo = typeInfo;
+ this.fieldAttribute = fieldAttribute;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
new file mode 100644
index 0000000000..3ea781252f
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
@@ -0,0 +1,252 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+namespace System.Diagnostics.Tracing
+{
+ /// <summary>
+ /// Holds property values of any type. For common value types, we have inline storage so that we don't need
+ /// to box the values. For all other types, we store the value in a single object reference field.
+ ///
+ /// To get the value of a property quickly, use a delegate produced by <see cref="PropertyValue.GetPropertyGetter(PropertyInfo)"/>.
+ /// </summary>
+ internal unsafe struct PropertyValue
+ {
+ /// <summary>
+ /// Union of well-known value types, to avoid boxing those types.
+ /// </summary>
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Scalar
+ {
+ [FieldOffset(0)]
+ public Boolean AsBoolean;
+ [FieldOffset(0)]
+ public Byte AsByte;
+ [FieldOffset(0)]
+ public SByte AsSByte;
+ [FieldOffset(0)]
+ public Char AsChar;
+ [FieldOffset(0)]
+ public Int16 AsInt16;
+ [FieldOffset(0)]
+ public UInt16 AsUInt16;
+ [FieldOffset(0)]
+ public Int32 AsInt32;
+ [FieldOffset(0)]
+ public UInt32 AsUInt32;
+ [FieldOffset(0)]
+ public Int64 AsInt64;
+ [FieldOffset(0)]
+ public UInt64 AsUInt64;
+ [FieldOffset(0)]
+ public IntPtr AsIntPtr;
+ [FieldOffset(0)]
+ public UIntPtr AsUIntPtr;
+ [FieldOffset(0)]
+ public Single AsSingle;
+ [FieldOffset(0)]
+ public Double AsDouble;
+ [FieldOffset(0)]
+ public Guid AsGuid;
+ [FieldOffset(0)]
+ public DateTime AsDateTime;
+ [FieldOffset(0)]
+ public DateTimeOffset AsDateTimeOffset;
+ [FieldOffset(0)]
+ public TimeSpan AsTimeSpan;
+ [FieldOffset(0)]
+ public Decimal AsDecimal;
+ }
+
+ // Anything not covered by the Scalar union gets stored in this reference.
+ readonly object _reference;
+ readonly Scalar _scalar;
+ readonly int _scalarLength;
+
+ private PropertyValue(object value)
+ {
+ _reference = value;
+ _scalar = default(Scalar);
+ _scalarLength = 0;
+ }
+
+ private PropertyValue(Scalar scalar, int scalarLength)
+ {
+ _reference = null;
+ _scalar = scalar;
+ _scalarLength = scalarLength;
+ }
+
+ private PropertyValue(Boolean value) : this(new Scalar() { AsBoolean = value }, sizeof(Boolean)) { }
+ private PropertyValue(Byte value) : this(new Scalar() { AsByte = value }, sizeof(Byte)) { }
+ private PropertyValue(SByte value) : this(new Scalar() { AsSByte = value }, sizeof(SByte)) { }
+ private PropertyValue(Char value) : this(new Scalar() { AsChar = value }, sizeof(Char)) { }
+ private PropertyValue(Int16 value) : this(new Scalar() { AsInt16 = value }, sizeof(Int16)) { }
+ private PropertyValue(UInt16 value) : this(new Scalar() { AsUInt16 = value }, sizeof(UInt16)) { }
+ private PropertyValue(Int32 value) : this(new Scalar() { AsInt32 = value }, sizeof(Int32)) { }
+ private PropertyValue(UInt32 value) : this(new Scalar() { AsUInt32 = value }, sizeof(UInt32)) { }
+ private PropertyValue(Int64 value) : this(new Scalar() { AsInt64 = value }, sizeof(Int64)) { }
+ private PropertyValue(UInt64 value) : this(new Scalar() { AsUInt64 = value }, sizeof(UInt64)) { }
+ private PropertyValue(IntPtr value) : this(new Scalar() { AsIntPtr = value }, sizeof(IntPtr)) { }
+ private PropertyValue(UIntPtr value) : this(new Scalar() { AsUIntPtr = value }, sizeof(UIntPtr)) { }
+ private PropertyValue(Single value) : this(new Scalar() { AsSingle = value }, sizeof(Single)) { }
+ private PropertyValue(Double value) : this(new Scalar() { AsDouble = value }, sizeof(Double)) { }
+ private PropertyValue(Guid value) : this(new Scalar() { AsGuid = value }, sizeof(Guid)) { }
+ private PropertyValue(DateTime value) : this(new Scalar() { AsDateTime = value }, sizeof(DateTime)) { }
+ private PropertyValue(DateTimeOffset value) : this(new Scalar() { AsDateTimeOffset = value }, sizeof(DateTimeOffset)) { }
+ private PropertyValue(TimeSpan value) : this(new Scalar() { AsTimeSpan = value }, sizeof(TimeSpan)) { }
+ private PropertyValue(Decimal value) : this(new Scalar() { AsDecimal = value }, sizeof(Decimal)) { }
+
+ public static Func<object, PropertyValue> GetFactory(Type type)
+ {
+ if (type == typeof(Boolean)) return value => new PropertyValue((Boolean)value);
+ if (type == typeof(Byte)) return value => new PropertyValue((Byte)value);
+ if (type == typeof(SByte)) return value => new PropertyValue((SByte)value);
+ if (type == typeof(Char)) return value => new PropertyValue((Char)value);
+ if (type == typeof(Int16)) return value => new PropertyValue((Int16)value);
+ if (type == typeof(UInt16)) return value => new PropertyValue((UInt16)value);
+ if (type == typeof(Int32)) return value => new PropertyValue((Int32)value);
+ if (type == typeof(UInt32)) return value => new PropertyValue((UInt32)value);
+ if (type == typeof(Int64)) return value => new PropertyValue((Int64)value);
+ if (type == typeof(UInt64)) return value => new PropertyValue((UInt64)value);
+ if (type == typeof(IntPtr)) return value => new PropertyValue((IntPtr)value);
+ if (type == typeof(UIntPtr)) return value => new PropertyValue((UIntPtr)value);
+ if (type == typeof(Single)) return value => new PropertyValue((Single)value);
+ if (type == typeof(Double)) return value => new PropertyValue((Double)value);
+ if (type == typeof(Guid)) return value => new PropertyValue((Guid)value);
+ if (type == typeof(DateTime)) return value => new PropertyValue((DateTime)value);
+ if (type == typeof(DateTimeOffset)) return value => new PropertyValue((DateTimeOffset)value);
+ if (type == typeof(TimeSpan)) return value => new PropertyValue((TimeSpan)value);
+ if (type == typeof(Decimal)) return value => new PropertyValue((Decimal)value);
+
+ return value => new PropertyValue(value);
+ }
+
+
+ public object ReferenceValue
+ {
+ get
+ {
+ Debug.Assert(_scalarLength == 0, "This ReflectedValue refers to an unboxed value type, not a reference type or boxed value type.");
+ return _reference;
+ }
+ }
+
+ public Scalar ScalarValue
+ {
+ get
+ {
+ Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type");
+ return _scalar;
+ }
+ }
+
+ public int ScalarLength
+ {
+ get
+ {
+ Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type");
+ return _scalarLength;
+ }
+ }
+
+ /// <summary>
+ /// Gets a delegate that gets the value of a given property.
+ /// </summary>
+ public static Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property)
+ {
+ if (property.DeclaringType.GetTypeInfo().IsValueType)
+ return GetBoxedValueTypePropertyGetter(property);
+ else
+ return GetReferenceTypePropertyGetter(property);
+ }
+
+ /// <summary>
+ /// Gets a delegate that gets the value of a property of a value type. We unfortunately cannot avoid boxing the value type,
+ /// without making this generic over the value type. That would result in a large number of generic instantiations, and furthermore
+ /// does not work correctly on .Net Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined
+ /// value types will be rare, and in any case the boxing only happens for events that are actually enabled.
+ /// </summary>
+ private static Func<PropertyValue, PropertyValue> GetBoxedValueTypePropertyGetter(PropertyInfo property)
+ {
+ var type = property.PropertyType;
+
+ if (type.GetTypeInfo().IsEnum)
+ type = Enum.GetUnderlyingType(type);
+
+ var factory = GetFactory(type);
+
+ return container => factory(property.GetValue(container.ReferenceValue));
+ }
+
+ /// <summary>
+ /// For properties of reference types, we use a generic helper class to get the value. This enables us to use MethodInfo.CreateDelegate
+ /// to build a fast getter. We can get away with this on .Net Native, because we really only need one runtime instantiation of the
+ /// generic type, since it's only instantiated over reference types (and thus all instances are shared).
+ /// </summary>
+ /// <param name="property"></param>
+ /// <returns></returns>
+ private static Func<PropertyValue, PropertyValue> GetReferenceTypePropertyGetter(PropertyInfo property)
+ {
+ var helper = (TypeHelper)Activator.CreateInstance(typeof(ReferenceTypeHelper<>).MakeGenericType(property.DeclaringType));
+ return helper.GetPropertyGetter(property);
+ }
+
+ private abstract class TypeHelper
+ {
+ public abstract Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property);
+
+ protected Delegate GetGetMethod(PropertyInfo property, Type propertyType)
+ {
+ return property.GetMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(property.DeclaringType, propertyType));
+ }
+ }
+
+ private sealed class ReferenceTypeHelper<TContainer> : TypeHelper where TContainer : class
+ {
+ public override Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property)
+ {
+ var type = property.PropertyType;
+
+ if (!Statics.IsValueType(type))
+ {
+ var getter = (Func<TContainer, object>)GetGetMethod(property, type);
+ return container => new PropertyValue(getter((TContainer)container.ReferenceValue));
+ }
+ else
+ {
+ if (type.GetTypeInfo().IsEnum)
+ type = Enum.GetUnderlyingType(type);
+
+ if (type == typeof(Boolean)) { var f = (Func<TContainer, Boolean>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Byte)) { var f = (Func<TContainer, Byte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(SByte)) { var f = (Func<TContainer, SByte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Char)) { var f = (Func<TContainer, Char>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int16)) { var f = (Func<TContainer, Int16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt16)) { var f = (Func<TContainer, UInt16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int32)) { var f = (Func<TContainer, Int32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt32)) { var f = (Func<TContainer, UInt32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int64)) { var f = (Func<TContainer, Int64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt64)) { var f = (Func<TContainer, UInt64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(IntPtr)) { var f = (Func<TContainer, IntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UIntPtr)) { var f = (Func<TContainer, UIntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Single)) { var f = (Func<TContainer, Single>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Double)) { var f = (Func<TContainer, Double>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Guid)) { var f = (Func<TContainer, Guid>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(DateTime)) { var f = (Func<TContainer, DateTime>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(DateTimeOffset)) { var f = (Func<TContainer, DateTimeOffset>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(TimeSpan)) { var f = (Func<TContainer, TimeSpan>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Decimal)) { var f = (Func<TContainer, Decimal>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+
+ return container => new PropertyValue(property.GetValue(container.ReferenceValue));
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs
new file mode 100644
index 0000000000..cdced968f0
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using 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 static class SimpleEventTypes<T>
+ {
+ private static TraceLoggingEventTypes instance;
+
+ public static TraceLoggingEventTypes Instance
+ {
+ get { return instance ?? InitInstance(); }
+ }
+
+ private static TraceLoggingEventTypes InitInstance()
+ {
+ var info = TraceLoggingTypeInfo.GetInstance(typeof(T), null);
+ var newInstance = new TraceLoggingEventTypes(info.Name, info.Tags, new TraceLoggingTypeInfo[] { info });
+ Interlocked.CompareExchange(ref instance, newInstance, null);
+ return instance;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
new file mode 100644
index 0000000000..901a0ed1a2
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
@@ -0,0 +1,297 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Diagnostics;
+
+#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: Type handler for empty or unsupported types.
+ /// </summary>
+ internal sealed class NullTypeInfo : TraceLoggingTypeInfo
+ {
+ public NullTypeInfo() : base(typeof(EmptyStruct)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddGroup(name);
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ return;
+ }
+
+ public override object GetData(object value)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Type handler for simple scalar types.
+ /// </summary>
+ sealed class ScalarTypeInfo : TraceLoggingTypeInfo
+ {
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc;
+ TraceLoggingDataType nativeFormat;
+
+ private ScalarTypeInfo(
+ Type type,
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc,
+ TraceLoggingDataType nativeFormat)
+ : base(type)
+ {
+ this.formatFunc = formatFunc;
+ this.nativeFormat = nativeFormat;
+ }
+
+ public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format)
+ {
+ collector.AddScalar(name, formatFunc(format, nativeFormat));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddScalar(value);
+ }
+
+ public static TraceLoggingTypeInfo Boolean() { return new ScalarTypeInfo(typeof(Boolean), Statics.Format8, TraceLoggingDataType.Boolean8); }
+ public static TraceLoggingTypeInfo Byte() { return new ScalarTypeInfo(typeof(Byte), Statics.Format8, TraceLoggingDataType.UInt8); }
+ public static TraceLoggingTypeInfo SByte() { return new ScalarTypeInfo(typeof(SByte), Statics.Format8, TraceLoggingDataType.Int8); }
+ public static TraceLoggingTypeInfo Char() { return new ScalarTypeInfo(typeof(Char), Statics.Format16, TraceLoggingDataType.Char16); }
+ public static TraceLoggingTypeInfo Int16() { return new ScalarTypeInfo(typeof(Int16), Statics.Format16, TraceLoggingDataType.Int16); }
+ public static TraceLoggingTypeInfo UInt16() { return new ScalarTypeInfo(typeof(UInt16), Statics.Format16, TraceLoggingDataType.UInt16); }
+ public static TraceLoggingTypeInfo Int32() { return new ScalarTypeInfo(typeof(Int32), Statics.Format32, TraceLoggingDataType.Int32); }
+ public static TraceLoggingTypeInfo UInt32() { return new ScalarTypeInfo(typeof(UInt32), Statics.Format32, TraceLoggingDataType.UInt32); }
+ public static TraceLoggingTypeInfo Int64() { return new ScalarTypeInfo(typeof(Int64), Statics.Format64, TraceLoggingDataType.Int64); }
+ public static TraceLoggingTypeInfo UInt64() { return new ScalarTypeInfo(typeof(UInt64), Statics.Format64, TraceLoggingDataType.UInt64); }
+ public static TraceLoggingTypeInfo IntPtr() { return new ScalarTypeInfo(typeof(IntPtr), Statics.FormatPtr, Statics.IntPtrType); }
+ public static TraceLoggingTypeInfo UIntPtr() { return new ScalarTypeInfo(typeof(UIntPtr), Statics.FormatPtr, Statics.UIntPtrType); }
+ public static TraceLoggingTypeInfo Single() { return new ScalarTypeInfo(typeof(Single), Statics.Format32, TraceLoggingDataType.Float); }
+ public static TraceLoggingTypeInfo Double() { return new ScalarTypeInfo(typeof(Double), Statics.Format64, TraceLoggingDataType.Double); }
+ public static TraceLoggingTypeInfo Guid() { return new ScalarTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid); }
+ }
+
+
+ /// <summary>
+ /// Type handler for arrays of scalars
+ /// </summary>
+ internal sealed class ScalarArrayTypeInfo : TraceLoggingTypeInfo
+ {
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc;
+ TraceLoggingDataType nativeFormat;
+ int elementSize;
+
+ private ScalarArrayTypeInfo(
+ Type type,
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc,
+ TraceLoggingDataType nativeFormat,
+ int elementSize)
+ : base(type)
+ {
+ this.formatFunc = formatFunc;
+ this.nativeFormat = nativeFormat;
+ this.elementSize = elementSize;
+ }
+
+ public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format)
+ {
+ collector.AddArray(name, formatFunc(format, nativeFormat));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddArray(value, elementSize);
+ }
+
+ public static TraceLoggingTypeInfo Boolean() { return new ScalarArrayTypeInfo(typeof(Boolean[]), Statics.Format8, TraceLoggingDataType.Boolean8, sizeof(Boolean)); }
+ public static TraceLoggingTypeInfo Byte() { return new ScalarArrayTypeInfo(typeof(Byte[]), Statics.Format8, TraceLoggingDataType.UInt8, sizeof(Byte)); }
+ public static TraceLoggingTypeInfo SByte() { return new ScalarArrayTypeInfo(typeof(SByte[]), Statics.Format8, TraceLoggingDataType.Int8, sizeof(SByte)); }
+ public static TraceLoggingTypeInfo Char() { return new ScalarArrayTypeInfo(typeof(Char[]), Statics.Format16, TraceLoggingDataType.Char16, sizeof(Char)); }
+ public static TraceLoggingTypeInfo Int16() { return new ScalarArrayTypeInfo(typeof(Int16[]), Statics.Format16, TraceLoggingDataType.Int16, sizeof(Int16)); }
+ public static TraceLoggingTypeInfo UInt16() { return new ScalarArrayTypeInfo(typeof(UInt16[]), Statics.Format16, TraceLoggingDataType.UInt16, sizeof(UInt16)); }
+ public static TraceLoggingTypeInfo Int32() { return new ScalarArrayTypeInfo(typeof(Int32[]), Statics.Format32, TraceLoggingDataType.Int32, sizeof(Int32)); }
+ public static TraceLoggingTypeInfo UInt32() { return new ScalarArrayTypeInfo(typeof(UInt32[]), Statics.Format32, TraceLoggingDataType.UInt32, sizeof(UInt32)); }
+ public static TraceLoggingTypeInfo Int64() { return new ScalarArrayTypeInfo(typeof(Int64[]), Statics.Format64, TraceLoggingDataType.Int64, sizeof(Int64)); }
+ public static TraceLoggingTypeInfo UInt64() { return new ScalarArrayTypeInfo(typeof(UInt64[]), Statics.Format64, TraceLoggingDataType.UInt64, sizeof(UInt64)); }
+ public static TraceLoggingTypeInfo IntPtr() { return new ScalarArrayTypeInfo(typeof(IntPtr[]), Statics.FormatPtr, Statics.IntPtrType, System.IntPtr.Size); }
+ public static TraceLoggingTypeInfo UIntPtr() { return new ScalarArrayTypeInfo(typeof(UIntPtr[]), Statics.FormatPtr, Statics.UIntPtrType, System.IntPtr.Size); }
+ public static TraceLoggingTypeInfo Single() { return new ScalarArrayTypeInfo(typeof(Single[]), Statics.Format32, TraceLoggingDataType.Float, sizeof(Single)); }
+ public static TraceLoggingTypeInfo Double() { return new ScalarArrayTypeInfo(typeof(Double[]), Statics.Format64, TraceLoggingDataType.Double, sizeof(Double)); }
+ public unsafe static TraceLoggingTypeInfo Guid() { return new ScalarArrayTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid, sizeof(Guid)); }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for String.
+ /// </summary>
+ internal sealed class StringTypeInfo : TraceLoggingTypeInfo
+ {
+ public StringTypeInfo() : base(typeof(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, PropertyValue value)
+ {
+ collector.AddBinary((string)value.ReferenceValue);
+ }
+
+ public override object GetData(object value)
+ {
+ if(value == null)
+ {
+ return "";
+ }
+
+ return value;
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for DateTime.
+ /// </summary>
+ internal sealed class DateTimeTypeInfo : TraceLoggingTypeInfo
+ {
+ public DateTimeTypeInfo() : base(typeof(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, PropertyValue value)
+ {
+ var ticks = value.ScalarValue.AsDateTime.Ticks;
+ collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for DateTimeOffset.
+ /// </summary>
+ internal sealed class DateTimeOffsetTypeInfo : TraceLoggingTypeInfo
+ {
+ public DateTimeOffsetTypeInfo() : base(typeof(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, PropertyValue value)
+ {
+ var dateTimeOffset = value.ScalarValue.AsDateTimeOffset;
+ var ticks = dateTimeOffset.Ticks;
+ collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000);
+ collector.AddScalar(dateTimeOffset.Offset.Ticks);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for TimeSpan.
+ /// </summary>
+ internal sealed class TimeSpanTypeInfo : TraceLoggingTypeInfo
+ {
+ public TimeSpanTypeInfo() : base(typeof(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, PropertyValue value)
+ {
+ collector.AddScalar(value.ScalarValue.AsTimeSpan.Ticks);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for Decimal. (Note: not full-fidelity, exposed as Double.)
+ /// </summary>
+ internal sealed class DecimalTypeInfo : TraceLoggingTypeInfo
+ {
+ public DecimalTypeInfo() : base(typeof(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, PropertyValue value)
+ {
+ collector.AddScalar((double)value.ScalarValue.AsDecimal);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for Nullable.
+ /// </summary>
+ internal sealed class NullableTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo valueInfo;
+ private readonly Func<PropertyValue, PropertyValue> hasValueGetter;
+ private readonly Func<PropertyValue, PropertyValue> valueGetter;
+
+ public NullableTypeInfo(Type type, List<Type> recursionCheck)
+ : base(type)
+ {
+ var typeArgs = type.GenericTypeArguments;
+ Debug.Assert(typeArgs.Length == 1);
+ this.valueInfo = TraceLoggingTypeInfo.GetInstance(typeArgs[0], recursionCheck);
+ this.hasValueGetter = PropertyValue.GetPropertyGetter(type.GetTypeInfo().GetDeclaredProperty("HasValue"));
+ this.valueGetter = PropertyValue.GetPropertyGetter(type.GetTypeInfo().GetDeclaredProperty("Value"));
+ }
+
+ 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, PropertyValue value)
+ {
+ var hasValue = hasValueGetter(value);
+ collector.AddScalar(hasValue);
+ var val = hasValue.ScalarValue.AsBoolean ? valueGetter(value) : valueInfo.PropertyValueFactory(Activator.CreateInstance(valueInfo.DataType));
+ this.valueInfo.WriteData(collector, val);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs
new file mode 100644
index 0000000000..9fa776753d
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs
@@ -0,0 +1,727 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Encoding = System.Text.Encoding;
+
+using Microsoft.Reflection;
+
+#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(nameof(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 = type.IsValueType();
+ return result;
+ }
+
+ public static bool IsEnum(Type type)
+ {
+ bool result = type.IsEnum();
+ return result;
+ }
+
+ public static IEnumerable<PropertyInfo> GetProperties(Type type)
+ {
+ IEnumerable<PropertyInfo> result = type.GetProperties();
+ return result;
+ }
+
+ public static MethodInfo GetGetMethod(PropertyInfo propInfo)
+ {
+ MethodInfo result = propInfo.GetGetMethod();
+ return result;
+ }
+
+ public static MethodInfo GetDeclaredStaticMethod(Type declaringType, string name)
+ {
+ MethodInfo result;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ 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 || ES_BUILD_PN)
+ 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 || ES_BUILD_PN)
+ 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 || ES_BUILD_PN)
+ 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)
+ {
+ return type.GetGenericArguments();
+ }
+
+ public static Type FindEnumerableElementType(Type type)
+ {
+ Type elementType = null;
+
+ if (IsGenericMatch(type, typeof(IEnumerable<>)))
+ {
+ elementType = GetGenericArguments(type)[0];
+ }
+ else
+ {
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ var ifaceTypes = type.GetTypeInfo().ImplementedInterfaces;
+#else
+ var ifaceTypes = type.FindInterfaces(IsGenericMatch, typeof(IEnumerable<>));
+#endif
+
+ foreach (var ifaceType in ifaceTypes)
+ {
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ 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)
+ {
+ return type.IsGenericType() && type.GetGenericTypeDefinition() == (Type)openType;
+ }
+
+ public static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo)
+ {
+ Delegate result;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ result = methodInfo.CreateDelegate(
+ delegateType);
+#else
+ result = Delegate.CreateDelegate(
+ delegateType,
+ methodInfo);
+#endif
+ return result;
+ }
+
+ public static TraceLoggingTypeInfo CreateDefaultTypeInfo(
+ Type dataType,
+ List<Type> recursionCheck)
+ {
+ TraceLoggingTypeInfo result;
+
+ if (recursionCheck.Contains(dataType))
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_RecursiveTypeDefinition"));
+ }
+
+ recursionCheck.Add(dataType);
+
+ var eventAttrib = Statics.GetCustomAttribute<EventDataAttribute>(dataType);
+ if (eventAttrib != null ||
+ Statics.GetCustomAttribute<CompilerGeneratedAttribute>(dataType) != null ||
+ IsGenericMatch(dataType, typeof(KeyValuePair<,>)))
+ {
+ 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 = ScalarArrayTypeInfo.Boolean();
+ }
+ else if (elementType == typeof(Byte))
+ {
+ result = ScalarArrayTypeInfo.Byte();
+ }
+ else if (elementType == typeof(SByte))
+ {
+ result = ScalarArrayTypeInfo.SByte();
+ }
+ else if (elementType == typeof(Int16))
+ {
+ result = ScalarArrayTypeInfo.Int16();
+ }
+ else if (elementType == typeof(UInt16))
+ {
+ result = ScalarArrayTypeInfo.UInt16();
+ }
+ else if (elementType == typeof(Int32))
+ {
+ result = ScalarArrayTypeInfo.Int32();
+ }
+ else if (elementType == typeof(UInt32))
+ {
+ result = ScalarArrayTypeInfo.UInt32();
+ }
+ else if (elementType == typeof(Int64))
+ {
+ result = ScalarArrayTypeInfo.Int64();
+ }
+ else if (elementType == typeof(UInt64))
+ {
+ result = ScalarArrayTypeInfo.UInt64();
+ }
+ else if (elementType == typeof(Char))
+ {
+ result = ScalarArrayTypeInfo.Char();
+ }
+ else if (elementType == typeof(Double))
+ {
+ result = ScalarArrayTypeInfo.Double();
+ }
+ else if (elementType == typeof(Single))
+ {
+ result = ScalarArrayTypeInfo.Single();
+ }
+ else if (elementType == typeof(IntPtr))
+ {
+ result = ScalarArrayTypeInfo.IntPtr();
+ }
+ else if (elementType == typeof(UIntPtr))
+ {
+ result = ScalarArrayTypeInfo.UIntPtr();
+ }
+ else if (elementType == typeof(Guid))
+ {
+ result = ScalarArrayTypeInfo.Guid();
+ }
+ else
+ {
+ result = new ArrayTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck));
+ }
+ }
+ else
+ {
+ if (Statics.IsEnum(dataType))
+ dataType = Enum.GetUnderlyingType(dataType);
+
+ if (dataType == typeof(String))
+ {
+ result = new StringTypeInfo();
+ }
+ else if (dataType == typeof(Boolean))
+ {
+ result = ScalarTypeInfo.Boolean();
+ }
+ else if (dataType == typeof(Byte))
+ {
+ result = ScalarTypeInfo.Byte();
+ }
+ else if (dataType == typeof(SByte))
+ {
+ result = ScalarTypeInfo.SByte();
+ }
+ else if (dataType == typeof(Int16))
+ {
+ result = ScalarTypeInfo.Int16();
+ }
+ else if (dataType == typeof(UInt16))
+ {
+ result = ScalarTypeInfo.UInt16();
+ }
+ else if (dataType == typeof(Int32))
+ {
+ result = ScalarTypeInfo.Int32();
+ }
+ else if (dataType == typeof(UInt32))
+ {
+ result = ScalarTypeInfo.UInt32();
+ }
+ else if (dataType == typeof(Int64))
+ {
+ result = ScalarTypeInfo.Int64();
+ }
+ else if (dataType == typeof(UInt64))
+ {
+ result = ScalarTypeInfo.UInt64();
+ }
+ else if (dataType == typeof(Char))
+ {
+ result = ScalarTypeInfo.Char();
+ }
+ else if (dataType == typeof(Double))
+ {
+ result = ScalarTypeInfo.Double();
+ }
+ else if (dataType == typeof(Single))
+ {
+ result = ScalarTypeInfo.Single();
+ }
+ else if (dataType == typeof(DateTime))
+ {
+ result = new DateTimeTypeInfo();
+ }
+ else if (dataType == typeof(Decimal))
+ {
+ result = new DecimalTypeInfo();
+ }
+ else if (dataType == typeof(IntPtr))
+ {
+ result = ScalarTypeInfo.IntPtr();
+ }
+ else if (dataType == typeof(UIntPtr))
+ {
+ result = ScalarTypeInfo.UIntPtr();
+ }
+ else if (dataType == typeof(Guid))
+ {
+ result = ScalarTypeInfo.Guid();
+ }
+ else if (dataType == typeof(TimeSpan))
+ {
+ result = new TimeSpanTypeInfo();
+ }
+ else if (dataType == typeof(DateTimeOffset))
+ {
+ result = new DateTimeOffsetTypeInfo();
+ }
+ else if (dataType == typeof(EmptyStruct))
+ {
+ result = new NullTypeInfo();
+ }
+ else if (IsGenericMatch(dataType, typeof(Nullable<>)))
+ {
+ result = new NullableTypeInfo(dataType, recursionCheck);
+ }
+ else
+ {
+ var elementType = FindEnumerableElementType(dataType);
+ if (elementType != null)
+ {
+ result = new EnumerableTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck));
+ }
+ else
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs
new file mode 100644
index 0000000000..04a047fb35
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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>
+ 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;
+ }
+
+ public void AddScalar(PropertyValue value)
+ {
+ var scalar = value.ScalarValue;
+ DataCollector.ThreadInstance.AddScalar(&scalar, value.ScalarLength);
+ }
+
+ /// <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 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 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);
+ }
+
+ public void AddArray(PropertyValue value, int elementSize)
+ {
+ Array array = (Array)value.ReferenceValue;
+ DataCollector.ThreadInstance.AddArray(array, array == null ? 0 : array.Length, elementSize);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
new file mode 100644
index 0000000000..529948daf8
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
@@ -0,0 +1,349 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
new file mode 100644
index 0000000000..e73339949a
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -0,0 +1,890 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
+// It is available from http://www.codeplex.com/hyperAddin
+
+#if PLATFORM_WINDOWS
+#define FEATURE_MANAGED_ETW
+
+#if !ES_BUILD_STANDALONE
+#define FEATURE_ACTIVITYSAMPLING
+#endif
+#endif // PLATFORM_WINDOWS
+
+#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;
+using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
+#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
+ {
+#if FEATURE_MANAGED_ETW
+ private byte[] providerMetadata;
+#endif
+
+ /// <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(nameof(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>
+ public unsafe void Write(string eventName)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ Contract.EndContractBlock();
+
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ var options = new EventSourceOptions();
+ this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance);
+ }
+
+ /// <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>
+ public unsafe void Write(string eventName, EventSourceOptions options)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ Contract.EndContractBlock();
+
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance);
+ }
+
+ /// <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>
+ public unsafe void Write<T>(
+ string eventName,
+ T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ var options = new EventSourceOptions();
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <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>
+ public unsafe void Write<T>(
+ string eventName,
+ EventSourceOptions options,
+ T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <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>
+ public unsafe void Write<T>(
+ string eventName,
+ ref EventSourceOptions options,
+ ref T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <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>
+ 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,
+ data,
+ pActivity,
+ relatedActivityId == Guid.Empty ? null : pRelated,
+ SimpleEventTypes<T>.Instance);
+ }
+ }
+
+ /// <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="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null) </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>
+ 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="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null)
+ /// </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>
+ private unsafe void WriteMultiMergeInner(
+ string eventName,
+ ref EventSourceOptions options,
+ TraceLoggingEventTypes eventTypes,
+ Guid* activityID,
+ Guid* childActivityID,
+ params object[] values)
+ {
+#if FEATURE_MANAGED_ETW
+ 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 && !ES_BUILD_PN)
+ 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++)
+ {
+ var info = eventTypes.typeInfos[i];
+ info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(values[i]));
+ }
+
+ this.WriteEventRaw(
+ eventName,
+ ref descriptor,
+ activityID,
+ childActivityID,
+ (int)(DataCollector.ThreadInstance.Finish() - descriptors),
+ (IntPtr)descriptors);
+ }
+ finally
+ {
+ this.WriteCleanup(pins, pinCount);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ /// <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="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null)
+ /// </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>
+ internal unsafe void WriteMultiMerge(
+ string eventName,
+ ref EventSourceOptions options,
+ TraceLoggingEventTypes eventTypes,
+ Guid* activityID,
+ Guid* childActivityID,
+ EventData* data)
+ {
+#if FEATURE_MANAGED_ETW
+ 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(
+ eventName,
+ ref descriptor,
+ activityID,
+ childActivityID,
+ numDescrs,
+ (IntPtr)descriptors);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ private unsafe void WriteImpl(
+ string eventName,
+ ref EventSourceOptions options,
+ object data,
+ Guid* pActivityId,
+ Guid* pRelatedActivityId,
+ TraceLoggingEventTypes eventTypes)
+ {
+ try
+ {
+ 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;
+ }
+
+#if FEATURE_MANAGED_ETW
+ 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);
+#endif // FEATURE_MANAGED_ETW
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ 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
+ {
+#if FEATURE_MANAGED_ETW
+ DataCollector.ThreadInstance.Enable(
+ scratch,
+ eventTypes.scratchSize,
+ descriptors + 3,
+ eventTypes.dataCount,
+ pins,
+ pinCount);
+
+ var info = eventTypes.typeInfos[0];
+ info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(data));
+
+ this.WriteEventRaw(
+ eventName,
+ ref descriptor,
+ pActivityId,
+ pRelatedActivityId,
+ (int)(DataCollector.ThreadInstance.Finish() - descriptors),
+ (IntPtr)descriptors);
+#endif // FEATURE_MANAGED_ETW
+
+ // TODO enable filtering for listeners.
+ if (m_Dispatchers != null)
+ {
+ var eventData = (EventPayload)(eventTypes.typeInfos[0].GetData(data));
+ WriteToAllListeners(eventName, ref descriptor, nameInfo.tags, pActivityId, eventData);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(eventName, ex);
+ }
+#if FEATURE_MANAGED_ETW
+ finally
+ {
+ this.WriteCleanup(pins, pinCount);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(eventName, ex);
+ }
+ }
+
+ private unsafe void WriteToAllListeners(string eventName, ref EventDescriptor eventDescriptor, EventTags tags, Guid* pActivityId, EventPayload payload)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventName = eventName;
+ eventCallbackArgs.m_level = (EventLevel) eventDescriptor.Level;
+ eventCallbackArgs.m_keywords = (EventKeywords) eventDescriptor.Keywords;
+ eventCallbackArgs.m_opcode = (EventOpcode) eventDescriptor.Opcode;
+ eventCallbackArgs.m_tags = tags;
+
+ // 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);
+ }
+
+ DispatchToAllListeners(-1, pActivityId, eventCallbackArgs);
+ }
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ [System.Runtime.ConstrainedExecution.ReliabilityContract(
+ System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState,
+ System.Runtime.ConstrainedExecution.Cer.Success)]
+#endif
+ [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 FEATURE_MANAGED_ETW
+ 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_", StringComparison.Ordinal))
+ {
+ string etwTrait = m_traits[i].Substring(4);
+ byte traitNum;
+ if (!byte.TryParse(etwTrait, out traitNum))
+ {
+ if (etwTrait == "GROUP")
+ {
+ traitNum = 1;
+ }
+ else
+ {
+ throw new ArgumentException(Resources.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);
+#endif //FEATURE_MANAGED_ETW
+ }
+
+ 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] != ' ') // Skip spaces between bytes.
+ {
+ if (!(i + 1 < value.Length))
+ {
+ throw new ArgumentException(Resources.GetResourceString("EvenHexDigits"), "traits");
+ }
+ metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1])));
+ i++;
+ }
+ }
+ }
+ else if ('A' <= firstChar || ' ' == firstChar) // Is it alphabetic or space (excludes digits and most punctuation).
+ {
+ metaData.AddRange(Encoding.UTF8.GetBytes(value));
+ }
+ else
+ {
+ throw new ArgumentException(Resources.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(Resources.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/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs
new file mode 100644
index 0000000000..e808a8823c
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+#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/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs
new file mode 100644
index 0000000000..c2239671bb
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs
@@ -0,0 +1,262 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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>
+ public 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(nameof(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(nameof(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(nameof(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] = TraceLoggingTypeInfo.GetInstance(paramInfos[i].ParameterType, recursionCheck);
+ }
+
+ return result;
+ }
+
+ private static TraceLoggingTypeInfo[] MakeArray(Type[] types)
+ {
+ if (types == null)
+ {
+ throw new ArgumentNullException(nameof(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] = TraceLoggingTypeInfo.GetInstance(types[i], recursionCheck);
+ }
+
+ return result;
+ }
+
+ private static TraceLoggingTypeInfo[] MakeArray(
+ TraceLoggingTypeInfo[] typeInfos)
+ {
+ if (typeInfos == null)
+ {
+ throw new ArgumentNullException(nameof(typeInfos));
+ }
+
+ Contract.EndContractBlock();
+
+ return (TraceLoggingTypeInfo[])typeInfos.Clone(); ;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs
new file mode 100644
index 0000000000..41225c8626
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs
@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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,
+ this.Tags,
+ 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(nameof(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(nameof(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(nameof(type));
+ }
+
+ if (this.BeginningBufferedArray)
+ {
+ throw new NotSupportedException(Resources.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(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums"));
+ }
+
+ this.bufferedArrayFieldCount = 0;
+ this.impl.BeginBuffered();
+ }
+
+ public void EndBufferedArray()
+ {
+ if (this.bufferedArrayFieldCount != 1)
+ {
+ throw new InvalidOperationException(Resources.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(Resources.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/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
new file mode 100644
index 0000000000..d68e106b0b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
@@ -0,0 +1,209 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+#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&lt;DataType>. Do not derive
+ /// from this class. Instead, derive from TraceLoggingTypeInfo&lt;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;
+ private readonly Func<object, PropertyValue> propertyValueFactory;
+
+ internal TraceLoggingTypeInfo(Type dataType)
+ {
+ if (dataType == null)
+ {
+ throw new ArgumentNullException(nameof(dataType));
+ }
+
+ Contract.EndContractBlock();
+
+ this.name = dataType.Name;
+ this.dataType = dataType;
+ this.propertyValueFactory = PropertyValue.GetFactory(dataType);
+ }
+
+ internal TraceLoggingTypeInfo(
+ Type dataType,
+ string name,
+ EventLevel level,
+ EventOpcode opcode,
+ EventKeywords keywords,
+ EventTags tags)
+ {
+ if (dataType == null)
+ {
+ throw new ArgumentNullException(nameof(dataType));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Contract.EndContractBlock();
+
+ Statics.CheckName(name);
+
+ this.name = name;
+ this.keywords = keywords;
+ this.level = level;
+ this.opcode = opcode;
+ this.tags = tags;
+ this.dataType = dataType;
+ this.propertyValueFactory = PropertyValue.GetFactory(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; }
+ }
+
+ internal Func<object, PropertyValue> PropertyValueFactory
+ {
+ get { return this.propertyValueFactory; }
+ }
+
+ /// <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&lt;SubType&gt;.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 WriteData(
+ TraceLoggingDataCollector collector,
+ PropertyValue 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;
+ }
+
+ [ThreadStatic] // per-thread cache to avoid synchronization
+ private static Dictionary<Type, TraceLoggingTypeInfo> threadCache;
+
+ public static TraceLoggingTypeInfo GetInstance(Type type, List<Type> recursionCheck)
+ {
+ var cache = threadCache ?? (threadCache = new Dictionary<Type, TraceLoggingTypeInfo>());
+
+ TraceLoggingTypeInfo instance;
+ if (!cache.TryGetValue(type, out instance))
+ {
+ if (recursionCheck == null)
+ recursionCheck = new List<Type>();
+ var recursionCheckCount = recursionCheck.Count;
+ instance = Statics.CreateDefaultTypeInfo(type, recursionCheck);
+ cache[type] = instance;
+ recursionCheck.RemoveRange(recursionCheckCount, recursionCheck.Count - recursionCheckCount);
+ }
+ return instance;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs
new file mode 100644
index 0000000000..42cdde5f6a
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.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 = TraceLoggingTypeInfo.GetInstance(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,
+ propertyInfo,
+ 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;
+ }
+ }
+ }
+}