Imported Upstream version 4.3.2.467

Former-commit-id: 9c2cb47f45fa221e661ab616387c9cda183f283d
This commit is contained in:
Xamarin Public Jenkins
2016-02-22 11:00:01 -05:00
parent f302175246
commit f3e3aab35a
4097 changed files with 122406 additions and 82300 deletions

View File

@@ -0,0 +1,350 @@
// Copyright (c) Microsoft Corporation. All rights reserved
using System;
using System.Collections.Generic;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing.Internal
#else
namespace System.Diagnostics.Tracing.Internal
#endif
{
#if ES_BUILD_AGAINST_DOTNET_V35
using Microsoft.Internal;
#endif
using Microsoft.Reflection;
using System.Reflection;
internal static class Environment
{
public static readonly string NewLine = System.Environment.NewLine;
public static int TickCount
{ get { return System.Environment.TickCount; } }
public static string GetResourceString(string key, params object[] args)
{
string fmt = rm.GetString(key);
if (fmt != null)
return string.Format(fmt, args);
string sargs = String.Empty;
foreach(var arg in args)
{
if (sargs != String.Empty)
sargs += ", ";
sargs += arg.ToString();
}
return key + " (" + sargs + ")";
}
public static string GetRuntimeResourceString(string key, params object[] args)
{
return GetResourceString(key, args);
}
private static System.Resources.ResourceManager rm = new System.Resources.ResourceManager("Microsoft.Diagnostics.Tracing.Messages", typeof(Environment).Assembly());
}
}
#if ES_BUILD_AGAINST_DOTNET_V35
namespace Microsoft.Diagnostics.Contracts.Internal
{
internal class Contract
{
public static void Assert(bool invariant)
{
Assert(invariant, string.Empty);
}
public static void Assert(bool invariant, string message)
{
if (!invariant)
{
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
throw new Exception("Assertion failed: " + message);
}
}
public static void EndContractBlock()
{ }
}
}
namespace Microsoft.Internal
{
using System.Text;
internal static class Tuple
{
public static Tuple<T1> Create<T1>(T1 item1)
{
return new Tuple<T1>(item1);
}
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
{
return new Tuple<T1, T2>(item1, item2);
}
}
[Serializable]
internal class Tuple<T1>
{
private readonly T1 m_Item1;
public T1 Item1 { get { return m_Item1; } }
public Tuple(T1 item1)
{
m_Item1 = item1;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("(");
sb.Append(m_Item1);
sb.Append(")");
return sb.ToString();
}
int Size
{
get
{
return 1;
}
}
}
[Serializable]
public class Tuple<T1, T2>
{
private readonly T1 m_Item1;
private readonly T2 m_Item2;
public T1 Item1 { get { return m_Item1; } }
public T2 Item2 { get { return m_Item2; } }
public Tuple(T1 item1, T2 item2)
{
m_Item1 = item1;
m_Item2 = item2;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("(");
sb.Append(m_Item1);
sb.Append(", ");
sb.Append(m_Item2);
sb.Append(")");
return sb.ToString();
}
int Size
{
get
{
return 2;
}
}
}
}
#endif
namespace Microsoft.Reflection
{
using System.Reflection;
#if ES_BUILD_PCL
[Flags]
public enum BindingFlags
{
DeclaredOnly = 0x02, // Only look at the members declared on the Type
Instance = 0x04, // Include Instance members in search
Static = 0x08, // Include Static members in search
Public = 0x10, // Include Public members in search
NonPublic = 0x20, // Include Non-Public members in search
}
public enum TypeCode {
Empty = 0, // Null reference
Object = 1, // Instance that isn't a value
DBNull = 2, // Database null value
Boolean = 3, // Boolean
Char = 4, // Unicode character
SByte = 5, // Signed 8-bit integer
Byte = 6, // Unsigned 8-bit integer
Int16 = 7, // Signed 16-bit integer
UInt16 = 8, // Unsigned 16-bit integer
Int32 = 9, // Signed 32-bit integer
UInt32 = 10, // Unsigned 32-bit integer
Int64 = 11, // Signed 64-bit integer
UInt64 = 12, // Unsigned 64-bit integer
Single = 13, // IEEE 32-bit float
Double = 14, // IEEE 64-bit double
Decimal = 15, // Decimal
DateTime = 16, // DateTime
String = 18, // Unicode character string
}
#endif
static class ReflectionExtensions
{
#if !ES_BUILD_PCL
//
// Type extension methods
//
public static bool IsEnum(this Type type) { return type.IsEnum; }
public static bool IsAbstract(this Type type) { return type.IsAbstract; }
public static bool IsSealed(this Type type) { return type.IsSealed; }
public static Type BaseType(this Type type) { return type.BaseType; }
public static Assembly Assembly(this Type type) { return type.Assembly; }
public static TypeCode GetTypeCode(this Type type) { return Type.GetTypeCode(type); }
public static bool ReflectionOnly(this Assembly assm) { return assm.ReflectionOnly; }
#else // ES_BUILD_PCL
//
// Type extension methods
//
public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; }
public static bool IsAbstract(this Type type) { return type.GetTypeInfo().IsAbstract; }
public static bool IsSealed(this Type type) { return type.GetTypeInfo().IsSealed; }
public static Type BaseType(this Type type) { return type.GetTypeInfo().BaseType; }
public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; }
public static MethodInfo[] GetMethods(this Type type, BindingFlags flags)
{
// Minimal implementation to cover only the cases we need
System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0);
System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly|BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic)) == 0);
Func<MethodInfo, bool> visFilter;
Func<MethodInfo, bool> instFilter;
switch (flags & (BindingFlags.Public | BindingFlags.NonPublic))
{
case 0: visFilter = mi => false; break;
case BindingFlags.Public: visFilter = mi => mi.IsPublic; break;
case BindingFlags.NonPublic: visFilter = mi => !mi.IsPublic; break;
default: visFilter = mi => true; break;
}
switch (flags & (BindingFlags.Instance | BindingFlags.Static))
{
case 0: instFilter = mi => false; break;
case BindingFlags.Instance: instFilter = mi => !mi.IsStatic; break;
case BindingFlags.Static: instFilter = mi => mi.IsStatic; break;
default: instFilter = mi => true; break;
}
List<MethodInfo> methodInfos = new List<MethodInfo>();
foreach (var declaredMethod in type.GetTypeInfo().DeclaredMethods)
{
if (visFilter(declaredMethod) && instFilter(declaredMethod))
methodInfos.Add(declaredMethod);
}
return methodInfos.ToArray();
}
public static FieldInfo[] GetFields(this Type type, BindingFlags flags)
{
// Minimal implementation to cover only the cases we need
System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0);
System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) == 0);
Func<FieldInfo, bool> visFilter;
Func<FieldInfo, bool> instFilter;
switch (flags & (BindingFlags.Public | BindingFlags.NonPublic))
{
case 0: visFilter = fi => false; break;
case BindingFlags.Public: visFilter = fi => fi.IsPublic; break;
case BindingFlags.NonPublic: visFilter = fi => !fi.IsPublic; break;
default: visFilter = fi => true; break;
}
switch (flags & (BindingFlags.Instance | BindingFlags.Static))
{
case 0: instFilter = fi => false; break;
case BindingFlags.Instance: instFilter = fi => !fi.IsStatic; break;
case BindingFlags.Static: instFilter = fi => fi.IsStatic; break;
default: instFilter = fi => true; break;
}
List<FieldInfo> fieldInfos = new List<FieldInfo>();
foreach (var declaredField in type.GetTypeInfo().DeclaredFields)
{
if (visFilter(declaredField) && instFilter(declaredField))
fieldInfos.Add(declaredField);
}
return fieldInfos.ToArray();
}
public static Type GetNestedType(this Type type, string nestedTypeName)
{
TypeInfo ti = null;
foreach(var nt in type.GetTypeInfo().DeclaredNestedTypes)
{
if (nt.Name == nestedTypeName)
{
ti = nt;
break;
}
}
return ti == null ? null : ti.AsType();
}
public static TypeCode GetTypeCode(this Type type)
{
if (type == typeof(bool)) return TypeCode.Boolean;
else if (type == typeof(byte)) return TypeCode.Byte;
else if (type == typeof(char)) return TypeCode.Char;
else if (type == typeof(ushort)) return TypeCode.UInt16;
else if (type == typeof(uint)) return TypeCode.UInt32;
else if (type == typeof(ulong)) return TypeCode.UInt64;
else if (type == typeof(sbyte)) return TypeCode.SByte;
else if (type == typeof(short)) return TypeCode.Int16;
else if (type == typeof(int)) return TypeCode.Int32;
else if (type == typeof(long)) return TypeCode.Int64;
else if (type == typeof(string)) return TypeCode.String;
else if (type == typeof(float)) return TypeCode.Single;
else if (type == typeof(double)) return TypeCode.Double;
else if (type == typeof(DateTime)) return TypeCode.DateTime;
else if (type == (typeof(Decimal))) return TypeCode.Decimal;
else return TypeCode.Object;
}
//
// FieldInfo extension methods
//
public static object GetRawConstantValue(this FieldInfo fi)
{ return fi.GetValue(null); }
//
// Assembly extension methods
//
public static bool ReflectionOnly(this Assembly assm)
{
// In PCL we can't load in reflection-only context
return false;
}
#endif
}
}
// Defining some no-ops in PCL builds
#if ES_BUILD_PCL
namespace System.Security
{
class SuppressUnmanagedCodeSecurityAttribute : Attribute { }
enum SecurityAction { Demand }
}
namespace System.Security.Permissions
{
class HostProtectionAttribute : Attribute { public bool MayLeakOnAbort { get; set; } }
class PermissionSetAttribute : Attribute
{
public PermissionSetAttribute(System.Security.SecurityAction action) { }
public bool Unrestricted { get; set; }
}
}
#endif

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
internal sealed class ArrayTypeInfo<ElementType>
: TraceLoggingTypeInfo<ElementType[]>
{
private readonly TraceLoggingTypeInfo<ElementType> elementInfo;
public ArrayTypeInfo(TraceLoggingTypeInfo<ElementType> elementInfo)
{
this.elementInfo = elementInfo;
}
public override void WriteMetadata(
TraceLoggingMetadataCollector collector,
string name,
EventFieldFormat format)
{
collector.BeginBufferedArray();
this.elementInfo.WriteMetadata(collector, name, format);
collector.EndBufferedArray();
}
public override void WriteData(
TraceLoggingDataCollector collector,
ref ElementType[] value)
{
var bookmark = collector.BeginBufferedArray();
var count = 0;
if (value != null)
{
count = value.Length;
for (int i = 0; i < value.Length; i++)
{
this.elementInfo.WriteData(collector, ref value[i]);
}
}
collector.EndBufferedArray(bookmark, count);
}
public override object GetData(object value)
{
var array = (ElementType[])value;
var serializedArray = new object[array.Length];
for (int i = 0; i < array.Length; i++)
{
serializedArray[i] = this.elementInfo.GetData(array[i]);
}
return serializedArray;
}
}
}

View File

@@ -0,0 +1,123 @@
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;
}
}
}

View File

@@ -0,0 +1,21 @@
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);
}
}

View File

@@ -0,0 +1,315 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: This is the implementation of the DataCollector
/// functionality. To enable safe access to the DataCollector from
/// untrusted code, there is one thread-local instance of this structure
/// per thread. The instance must be Enabled before any data is written to
/// it. The instance must be Finished before the data is passed to
/// EventWrite. The instance must be Disabled before the arrays referenced
/// by the pointers are freed or unpinned.
/// </summary>
[SecurityCritical]
internal unsafe struct DataCollector
{
[ThreadStatic]
internal static DataCollector ThreadInstance;
private byte* scratchEnd;
private EventSource.EventData* datasEnd;
private GCHandle* pinsEnd;
private EventSource.EventData* datasStart;
private byte* scratch;
private EventSource.EventData* datas;
private GCHandle* pins;
private byte[] buffer;
private int bufferPos;
private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this.
private bool writingScalars;
internal void Enable(
byte* scratch,
int scratchSize,
EventSource.EventData* datas,
int dataCount,
GCHandle* pins,
int pinCount)
{
this.datasStart = datas;
this.scratchEnd = scratch + scratchSize;
this.datasEnd = datas + dataCount;
this.pinsEnd = pins + pinCount;
this.scratch = scratch;
this.datas = datas;
this.pins = pins;
this.writingScalars = false;
}
internal void Disable()
{
this = new DataCollector();
}
/// <summary>
/// Completes the list of scalars. Finish must be called before the data
/// descriptor array is passed to EventWrite.
/// </summary>
/// <returns>
/// A pointer to the next unused data descriptor, or datasEnd if they were
/// all used. (Descriptors may be unused if a string or array was null.)
/// </returns>
internal EventSource.EventData* Finish()
{
this.ScalarsEnd();
return this.datas;
}
internal void AddScalar(void* value, int size)
{
var pb = (byte*)value;
if (this.bufferNesting == 0)
{
var scratchOld = this.scratch;
var scratchNew = scratchOld + size;
if (this.scratchEnd < scratchNew)
{
throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_AddScalarOutOfRange"));
}
this.ScalarsBegin();
this.scratch = scratchNew;
for (int i = 0; i != size; i++)
{
scratchOld[i] = pb[i];
}
}
else
{
var oldPos = this.bufferPos;
this.bufferPos = checked(this.bufferPos + size);
this.EnsureBuffer();
for (int i = 0; i != size; i++, oldPos++)
{
this.buffer[oldPos] = pb[i];
}
}
}
internal void AddBinary(string value, int size)
{
if (size > ushort.MaxValue)
{
size = ushort.MaxValue - 1;
}
if (this.bufferNesting != 0)
{
this.EnsureBuffer(size + 2);
}
this.AddScalar(&size, 2);
if (size != 0)
{
if (this.bufferNesting == 0)
{
this.ScalarsEnd();
this.PinArray(value, size);
}
else
{
var oldPos = this.bufferPos;
this.bufferPos = checked(this.bufferPos + size);
this.EnsureBuffer();
fixed (void* p = value)
{
Marshal.Copy((IntPtr)p, this.buffer, oldPos, size);
}
}
}
}
internal void AddBinary(Array value, int size)
{
this.AddArray(value, size, 1);
}
internal void AddArray(Array value, int length, int itemSize)
{
if (length > ushort.MaxValue)
{
length = ushort.MaxValue;
}
var size = length * itemSize;
if (this.bufferNesting != 0)
{
this.EnsureBuffer(size + 2);
}
this.AddScalar(&length, 2);
if (length != 0)
{
if (this.bufferNesting == 0)
{
this.ScalarsEnd();
this.PinArray(value, size);
}
else
{
var oldPos = this.bufferPos;
this.bufferPos = checked(this.bufferPos + size);
this.EnsureBuffer();
Buffer.BlockCopy(value, 0, this.buffer, oldPos, size);
}
}
}
/// <summary>
/// Marks the start of a non-blittable array or enumerable.
/// </summary>
/// <returns>Bookmark to be passed to EndBufferedArray.</returns>
internal int BeginBufferedArray()
{
this.BeginBuffered();
this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable)
return this.bufferPos;
}
/// <summary>
/// Marks the end of a non-blittable array or enumerable.
/// </summary>
/// <param name="bookmark">The value returned by BeginBufferedArray.</param>
/// <param name="count">The number of items in the array.</param>
internal void EndBufferedArray(int bookmark, int count)
{
this.EnsureBuffer();
this.buffer[bookmark - 2] = unchecked((byte)count);
this.buffer[bookmark - 1] = unchecked((byte)(count >> 8));
this.EndBuffered();
}
/// <summary>
/// Marks the start of dynamically-buffered data.
/// </summary>
internal void BeginBuffered()
{
this.ScalarsEnd();
this.bufferNesting += 1;
}
/// <summary>
/// Marks the end of dynamically-buffered data.
/// </summary>
internal void EndBuffered()
{
this.bufferNesting -= 1;
if (this.bufferNesting == 0)
{
/*
*/
this.EnsureBuffer();
this.PinArray(this.buffer, this.bufferPos);
this.buffer = null;
this.bufferPos = 0;
}
}
private void EnsureBuffer()
{
var required = this.bufferPos;
if (this.buffer == null || this.buffer.Length < required)
{
this.GrowBuffer(required);
}
}
private void EnsureBuffer(int additionalSize)
{
var required = this.bufferPos + additionalSize;
if (this.buffer == null || this.buffer.Length < required)
{
this.GrowBuffer(required);
}
}
private void GrowBuffer(int required)
{
var newSize = this.buffer == null ? 64 : this.buffer.Length;
do
{
newSize *= 2;
}
while (newSize < required);
Array.Resize(ref this.buffer, newSize);
}
private void PinArray(object value, int size)
{
var pinsTemp = this.pins;
if (this.pinsEnd <= pinsTemp)
{
throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_PinArrayOutOfRange"));
}
var datasTemp = this.datas;
if (this.datasEnd <= datasTemp)
{
throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
}
this.pins = pinsTemp + 1;
this.datas = datasTemp + 1;
*pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned);
datasTemp->m_Ptr = (long)(ulong)(UIntPtr)(void*)pinsTemp->AddrOfPinnedObject();
datasTemp->m_Size = size;
}
private void ScalarsBegin()
{
if (!this.writingScalars)
{
var datasTemp = this.datas;
if (this.datasEnd <= datasTemp)
{
throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
}
datasTemp->m_Ptr = (long)(ulong)(UIntPtr)this.scratch;
this.writingScalars = true;
}
}
private void ScalarsEnd()
{
if (this.writingScalars)
{
var datasTemp = this.datas;
datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr));
this.datas = datasTemp + 1;
this.writingScalars = false;
}
}
}
}

View File

@@ -0,0 +1,13 @@
#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
{
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Reflection;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// Provides support for casting enums to their underlying type
/// from within generic context.
/// </summary>
/// <typeparam name="UnderlyingType">
/// The underlying type of the enum.
/// </typeparam>
internal static class EnumHelper<UnderlyingType>
{
private delegate UnderlyingType Transformer<ValueType>(ValueType value);
private static readonly MethodInfo IdentityInfo =
Statics.GetDeclaredStaticMethod(typeof(EnumHelper<UnderlyingType>), "Identity");
public static UnderlyingType Cast<ValueType>(ValueType value)
{
return Caster<ValueType>.Instance(value);
}
internal static UnderlyingType Identity(UnderlyingType value)
{
return value;
}
private static class Caster<ValueType>
{
public static readonly Transformer<ValueType> Instance =
(Transformer<ValueType>)Statics.CreateDelegate(
typeof(Transformer<ValueType>),
IdentityInfo);
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
internal sealed class EnumerableTypeInfo<IterableType, ElementType>
: TraceLoggingTypeInfo<IterableType>
where IterableType : IEnumerable<ElementType>
{
private readonly TraceLoggingTypeInfo<ElementType> elementInfo;
public EnumerableTypeInfo(TraceLoggingTypeInfo<ElementType> elementInfo)
{
this.elementInfo = elementInfo;
}
public override void WriteMetadata(
TraceLoggingMetadataCollector collector,
string name,
EventFieldFormat format)
{
collector.BeginBufferedArray();
this.elementInfo.WriteMetadata(collector, name, format);
collector.EndBufferedArray();
}
public override void WriteData(
TraceLoggingDataCollector collector,
ref IterableType value)
{
var bookmark = collector.BeginBufferedArray();
var count = 0;
if (value != null)
{
foreach (var element in value)
{
var el = element;
this.elementInfo.WriteData(collector, ref el);
count++;
}
}
collector.EndBufferedArray(bookmark, count);
}
public override object GetData(object value)
{
var iterType = (IterableType)value;
List<object> serializedEnumerable = new List<object>();
foreach (var element in iterType)
{
serializedEnumerable.Add(elementInfo.GetData(element));
}
return serializedEnumerable.ToArray();
}
}
}

View File

@@ -0,0 +1,142 @@
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;
}
}
}

View File

@@ -0,0 +1,72 @@
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.
///
internal string Name
{
get;
set;
}
/// <summary>
/// Gets or sets a field formatting hint.
/// </summary>
public EventFieldFormat Format
{
get;
set;
}
}
}

View File

@@ -0,0 +1,126 @@
#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
}
}

View File

@@ -0,0 +1,21 @@
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
{
}
}

View File

@@ -0,0 +1,147 @@
using System.Collections.Generic;
using System.Collections;
#if !ES_BUILD_AGAINST_DOTNET_V35
using Contract = System.Diagnostics.Contracts.Contract;
#else
using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
#endif
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// EventPayload class holds the list of parameters and their corresponding values for user defined types passed to
/// EventSource APIs.
/// Preserving the order of the elements as they were found inside user defined types is the most important characteristic of this class.
/// </summary>
internal class EventPayload : IDictionary<string, object>
{
internal EventPayload(List<string> payloadNames, List<object> payloadValues)
{
Contract.Assert(payloadNames.Count == payloadValues.Count);
m_names = payloadNames;
m_values = payloadValues;
}
public ICollection<string> Keys { get { return m_names; } }
public ICollection<object> Values { get { return m_values; } }
public object this[string key]
{
get
{
if (key == null)
throw new System.ArgumentNullException("key");
int position = 0;
foreach(var name in m_names)
{
if (name == key)
{
return m_values[position];
}
position++;
}
throw new System.Collections.Generic.KeyNotFoundException();
}
set
{
throw new System.NotSupportedException();
}
}
public void Add(string key, object value)
{
throw new System.NotSupportedException();
}
public void Add(KeyValuePair<string, object> payloadEntry)
{
throw new System.NotSupportedException();
}
public void Clear()
{
throw new System.NotSupportedException();
}
public bool Contains(KeyValuePair<string, object> entry)
{
return ContainsKey(entry.Key);
}
public bool ContainsKey(string key)
{
if (key == null)
throw new System.ArgumentNullException("key");
foreach (var item in m_names)
{
if (item == key)
return true;
}
return false;
}
public int Count { get { return m_names.Count; } }
public bool IsReadOnly { get { return true; } }
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
throw new System.NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
var instance = this as IEnumerable<KeyValuePair<string, object>>;
return instance.GetEnumerator();
}
public void CopyTo(KeyValuePair<string, object>[] payloadEntries, int count)
{
throw new System.NotSupportedException();
}
public bool Remove(string key)
{
throw new System.NotSupportedException();
}
public bool Remove(KeyValuePair<string, object> entry)
{
throw new System.NotSupportedException();
}
public bool TryGetValue(string key, out object value)
{
if (key == null)
throw new System.ArgumentNullException("key");
int position = 0;
foreach (var name in m_names)
{
if (name == key)
{
value = m_values[position];
return true;
}
position++;
}
value = default(object);
return false;
}
#region private
private List<string> m_names;
private List<object> m_values;
#endregion
}
}

View File

@@ -0,0 +1,317 @@
using System;
#if !ES_BUILD_AGAINST_DOTNET_V35
using Contract = System.Diagnostics.Contracts.Contract;
#else
using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
#endif
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// Provides support for EventSource activities by marking the start and
/// end of a particular operation.
/// </summary>
internal sealed class EventSourceActivity
: IDisposable
{
/// <summary>
/// Initializes a new instance of the EventSourceActivity class that
/// is attached to the specified event source. The new activity will
/// not be attached to any related (parent) activity.
/// The activity is created in the Initialized state.
/// </summary>
/// <param name="eventSource">
/// The event source to which the activity information is written.
/// </param>
public EventSourceActivity(EventSource eventSource)
{
if (eventSource == null)
throw new ArgumentNullException("eventSource");
Contract.EndContractBlock();
this.eventSource = eventSource;
}
/// <summary>
/// You can make an activity out of just an EventSource.
/// </summary>
public static implicit operator EventSourceActivity(EventSource eventSource) { return new EventSourceActivity(eventSource); }
/* Properties */
/// <summary>
/// Gets the event source to which this activity writes events.
/// </summary>
public EventSource EventSource
{
get { return this.eventSource; }
}
/// <summary>
/// Gets this activity's unique identifier, or the default Guid if the
/// event source was disabled when the activity was initialized.
/// </summary>
public Guid Id
{
get { return this.activityId; }
}
#if false // don't expose RelatedActivityId unless there is a need.
/// <summary>
/// Gets the unique identifier of this activity's related (parent)
/// activity.
/// </summary>
public Guid RelatedId
{
get { return this.relatedActivityId; }
}
#endif
/// <summary>
/// Writes a Start event with the specified name and data. If the start event is not active (because the provider
/// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner
/// and it is effectively like the Start d
///
/// A new activityID GUID is generated and the returned
/// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes)
/// with this activityID. In addition the Start activity will log a 'relatedActivityID' that was the activity
/// ID before the start event. This way event processors can form a linked list of all the activities that
/// caused this one (directly or indirectly).
/// </summary>
/// <param name="eventName">
/// The name to use for the event. It is strongly suggested that this name end in 'Start' (e.g. DownloadStart).
/// If you do this, then the Stop() method will automatically replace the 'Start' suffix with a 'Stop' suffix.
/// </param>
/// <param name="options">Allow options (keywords, level) to be set for the write associated with this start
/// These will also be used for the stop event.</param>
/// <param name="data">The data to include in the event.</param>
public EventSourceActivity Start<T>(string eventName, EventSourceOptions options, T data)
{
return this.Start(eventName, ref options, ref data);
}
/// <summary>
/// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords
/// and level==Info) Data payload is empty.
/// </summary>
public EventSourceActivity Start(string eventName)
{
var options = new EventSourceOptions();
var data = new EmptyStruct();
return this.Start(eventName, ref options, ref data);
}
/// <summary>
/// Shortcut version see Start(string eventName, EventSourceOptions options, T data). Data payload is empty.
/// </summary>
public EventSourceActivity Start(string eventName, EventSourceOptions options)
{
var data = new EmptyStruct();
return this.Start(eventName, ref options, ref data);
}
/// <summary>
/// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords
/// and level==Info)
/// </summary>
public EventSourceActivity Start<T>(string eventName, T data)
{
var options = new EventSourceOptions();
return this.Start(eventName, ref options, ref data);
}
/// <summary>
/// Writes a Stop event with the specified data, and sets the activity
/// to the Stopped state. The name is determined by the eventName used in Start.
/// If that Start event name is suffixed with 'Start' that is removed, and regardless
/// 'Stop' is appended to the result to form the Stop event name.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="data">The data to include in the event.</param>
public void Stop<T>(T data)
{
this.Stop(null, ref data);
}
/// <summary>
/// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
/// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
/// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
/// </summary>
public void Stop<T>(string eventName)
{
var data = new EmptyStruct();
this.Stop(eventName, ref data);
}
/// <summary>
/// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
/// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
/// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
/// </summary>
public void Stop<T>(string eventName, T data)
{
this.Stop(eventName, ref data);
}
/// <summary>
/// Writes an event associated with this activity to the eventSource associted with this activity.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="eventName">
/// The name to use for the event. If null, the name is determined from
/// data's type.
/// </param>
/// <param name="options">
/// The options to use for the event.
/// </param>
/// <param name="data">The data to include in the event.</param>
public void Write<T>(string eventName, EventSourceOptions options, T data)
{
this.Write(this.eventSource, eventName, ref options, ref data);
}
/// <summary>
/// Writes an event associated with this activity.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="eventName">
/// The name to use for the event. If null, the name is determined from
/// data's type.
/// </param>
/// <param name="data">The data to include in the event.</param>
public void Write<T>(string eventName, T data)
{
var options = new EventSourceOptions();
this.Write(this.eventSource, eventName, ref options, ref data);
}
/// <summary>
/// Writes a trivial event associated with this activity.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="eventName">
/// The name to use for the event. Must not be null.
/// </param>
/// <param name="options">
/// The options to use for the event.
/// </param>
public void Write(string eventName, EventSourceOptions options)
{
var data = new EmptyStruct();
this.Write(this.eventSource, eventName, ref options, ref data);
}
/// <summary>
/// Writes a trivial event associated with this activity.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="eventName">
/// The name to use for the event. Must not be null.
/// </param>
public void Write(string eventName)
{
var options = new EventSourceOptions();
var data = new EmptyStruct();
this.Write(this.eventSource, eventName, ref options, ref data);
}
/// <summary>
/// Writes an event to a arbitrary eventSource stamped with the activity ID of this activity.
/// </summary>
public void Write<T>(EventSource source, string eventName, EventSourceOptions options, T data)
{
this.Write(source, eventName, ref options, ref data);
}
/// <summary>
/// Releases any unmanaged resources associated with this object.
/// If the activity is in the Started state, calls Stop().
/// </summary>
public void Dispose()
{
if (this.state == State.Started)
{
var data = new EmptyStruct();
this.Stop(null, ref data);
}
}
#region private
private EventSourceActivity Start<T>(string eventName, ref EventSourceOptions options, ref T data)
{
if (this.state != State.Started)
throw new InvalidOperationException();
// If the source is not on at all, then we don't need to do anything and we can simply return ourselves.
if (!this.eventSource.IsEnabled())
return this;
var newActivity = new EventSourceActivity(eventSource);
if (!this.eventSource.IsEnabled(options.Level, options.Keywords))
{
// newActivity.relatedActivityId = this.Id;
Guid relatedActivityId = this.Id;
newActivity.activityId = Guid.NewGuid();
newActivity.startStopOptions = options;
newActivity.eventName = eventName;
newActivity.startStopOptions.Opcode = EventOpcode.Start;
this.eventSource.Write(eventName, ref newActivity.startStopOptions, ref newActivity.activityId, ref relatedActivityId, ref data);
}
else
{
// If we are not active, we don't set the eventName, which basically also turns off the Stop event as well.
newActivity.activityId = this.Id;
}
return newActivity;
}
private void Write<T>(EventSource eventSource, string eventName, ref EventSourceOptions options, ref T data)
{
if (this.state != State.Started)
throw new InvalidOperationException(); // Write after stop.
if (eventName == null)
throw new ArgumentNullException();
eventSource.Write(eventName, ref options, ref this.activityId, ref s_empty, ref data);
}
private void Stop<T>(string eventName, ref T data)
{
if (this.state != State.Started)
throw new InvalidOperationException();
// If start was not fired, then stop isn't as well.
if (!StartEventWasFired)
return;
this.state = State.Stopped;
if (eventName == null)
{
eventName = this.eventName;
if (eventName.EndsWith("Start"))
eventName = eventName.Substring(0, eventName.Length - 5);
eventName = eventName + "Stop";
}
this.startStopOptions.Opcode = EventOpcode.Stop;
this.eventSource.Write(eventName, ref this.startStopOptions, ref this.activityId, ref s_empty, ref data);
}
private enum State
{
Started,
Stopped
}
/// <summary>
/// If eventName is non-null then we logged a start event
/// </summary>
private bool StartEventWasFired { get { return eventName != null; }}
private readonly EventSource eventSource;
private EventSourceOptions startStopOptions;
internal Guid activityId;
// internal Guid relatedActivityId;
private State state;
private string eventName;
static internal Guid s_empty;
#endregion
}
}

View File

@@ -0,0 +1,126 @@
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;
}
}
}
}

View File

@@ -0,0 +1,227 @@
using System;
using Encoding = System.Text.Encoding;
#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: Contains the information needed to generate tracelogging
/// metadata for an event field.
/// </summary>
internal class FieldMetadata
{
/// <summary>
/// Name of the field
/// </summary>
private readonly string name;
/// <summary>
/// The number of bytes in the UTF8 Encoding of 'name' INCLUDING a null terminator.
/// </summary>
private readonly int nameSize;
private readonly EventFieldTags tags;
private readonly byte[] custom;
/// <summary>
/// ETW supports fixed sized arrays. If inType has the InTypeFixedCountFlag then this is the
/// statically known count for the array. It is also used to encode the number of bytes of
/// custom meta-data if InTypeCustomCountFlag set.
/// </summary>
private readonly ushort fixedCount;
private byte inType;
private byte outType;
/// <summary>
/// Scalar or variable-length array.
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
bool variableCount)
: this(
name,
type,
tags,
variableCount ? Statics.InTypeVariableCountFlag : (byte)0,
0,
null)
{
return;
}
/// <summary>
/// Fixed-length array.
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
ushort fixedCount)
: this(
name,
type,
tags,
Statics.InTypeFixedCountFlag,
fixedCount,
null)
{
return;
}
/// <summary>
/// Custom serializer
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
byte[] custom)
: this(
name,
type,
tags,
Statics.InTypeCustomCountFlag,
checked((ushort)(custom == null ? 0 : custom.Length)),
custom)
{
return;
}
private FieldMetadata(
string name,
TraceLoggingDataType dataType,
EventFieldTags tags,
byte countFlags,
ushort fixedCount = 0,
byte[] custom = null)
{
if (name == null)
{
throw new ArgumentNullException(
"name",
"This usually means that the object passed to Write is of a type that"
+ " does not support being used as the top-level object in an event,"
+ " e.g. a primitive or built-in type.");
}
Statics.CheckName(name);
var coreType = (int)dataType & Statics.InTypeMask;
this.name = name;
this.nameSize = Encoding.UTF8.GetByteCount(this.name) + 1;
this.inType = (byte)(coreType | countFlags);
this.outType = (byte)(((int)dataType >> 8) & Statics.OutTypeMask);
this.tags = tags;
this.fixedCount = fixedCount;
this.custom = custom;
if (countFlags != 0)
{
if (coreType == (int)TraceLoggingDataType.Nil)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNil"));
}
if (coreType == (int)TraceLoggingDataType.Binary)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfBinary"));
}
#if !BROKEN_UNTIL_M3
if (coreType == (int)TraceLoggingDataType.Utf16String ||
coreType == (int)TraceLoggingDataType.MbcsString)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNullTerminatedString"));
}
#endif
}
if (((int)this.tags & 0xfffffff) != 0)
{
this.outType |= Statics.OutTypeChainFlag;
}
if (this.outType != 0)
{
this.inType |= Statics.InTypeChainFlag;
}
}
public void IncrementStructFieldCount()
{
this.inType |= Statics.InTypeChainFlag;
this.outType++;
if ((this.outType & Statics.OutTypeMask) == 0)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_TooManyFields"));
}
}
/// <summary>
/// This is the main routine for FieldMetaData. Basically it will serialize the data in
/// this structure as TraceLogging style meta-data into the array 'metaArray' starting at
/// 'pos' (pos is updated to reflect the bytes written).
///
/// Note that 'metaData' can be null, in which case it only updates 'pos'. This is useful
/// for a 'two pass' approach where you figure out how big to make the array, and then you
/// fill it in.
/// </summary>
public void Encode(ref int pos, byte[] metadata)
{
// Write out the null terminated UTF8 encoded name
if (metadata != null)
{
Encoding.UTF8.GetBytes(this.name, 0, this.name.Length, metadata, pos);
}
pos += this.nameSize;
// Write 1 byte for inType
if (metadata != null)
{
metadata[pos] = this.inType;
}
pos += 1;
// If InTypeChainFlag set, then write out the outType
if (0 != (this.inType & Statics.InTypeChainFlag))
{
if (metadata != null)
{
metadata[pos] = this.outType;
}
pos += 1;
// If OutTypeChainFlag set, then write out tags
if (0 != (this.outType & Statics.OutTypeChainFlag))
{
Statics.EncodeTags((int)this.tags, ref pos, metadata);
}
}
// If InTypeFixedCountFlag set, write out the fixedCount (2 bytes little endian)
if (0 != (this.inType & Statics.InTypeFixedCountFlag))
{
if (metadata != null)
{
metadata[pos + 0] = unchecked((byte)this.fixedCount);
metadata[pos + 1] = (byte)(this.fixedCount >> 8);
}
pos += 2;
// If InTypeCustomCountFlag set, write out the blob of custom meta-data.
if (Statics.InTypeCustomCountFlag == (this.inType & Statics.InTypeCountMask) &&
this.fixedCount != 0)
{
if (metadata != null)
{
Buffer.BlockCopy(this.custom, 0, metadata, pos, this.fixedCount);
}
pos += this.fixedCount;
}
}
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: An implementation of TraceLoggingTypeInfo that works
/// for arbitrary types. It writes all public instance properties of
/// the type. Implemented using Delegate.CreateDelegate(property.Getter).
/// </summary>
/// <typeparam name="ContainerType">
/// Type from which to read values.
/// </typeparam>
internal sealed class InvokeTypeInfo<ContainerType>
: TraceLoggingTypeInfo<ContainerType>
{
private readonly PropertyAnalysis[] properties;
private readonly PropertyAccessor<ContainerType>[] accessors;
public InvokeTypeInfo(
TypeAnalysis typeAnalysis)
: base(
typeAnalysis.name,
typeAnalysis.level,
typeAnalysis.opcode,
typeAnalysis.keywords,
typeAnalysis.tags)
{
if (typeAnalysis.properties.Length != 0)
{
this.properties = typeAnalysis.properties;
this.accessors = new PropertyAccessor<ContainerType>[this.properties.Length];
for (int i = 0; i < this.accessors.Length; i++)
{
this.accessors[i] = PropertyAccessor<ContainerType>.Create(this.properties[i]);
}
}
}
public override void WriteMetadata(
TraceLoggingMetadataCollector collector,
string name,
EventFieldFormat format)
{
var groupCollector = collector.AddGroup(name);
if (this.properties != null)
{
foreach (var property in this.properties)
{
var propertyFormat = EventFieldFormat.Default;
var propertyAttribute = property.fieldAttribute;
if (propertyAttribute != null)
{
groupCollector.Tags = propertyAttribute.Tags;
propertyFormat = propertyAttribute.Format;
}
property.typeInfo.WriteMetadata(
groupCollector,
property.name,
propertyFormat);
}
}
}
public override void WriteData(
TraceLoggingDataCollector collector,
ref ContainerType value)
{
if (this.accessors != null)
{
foreach (var accessor in this.accessors)
{
accessor.Write(collector, ref value);
}
}
}
public override object GetData(object value)
{
if (this.properties != null)
{
var membersNames = new List<string>();
var memebersValues = new List<object>();
for (int i = 0; i < this.properties.Length; i++)
{
var propertyValue = accessors[i].GetData((ContainerType)value);
membersNames.Add(properties[i].name);
memebersValues.Add(properties[i].typeInfo.GetData(propertyValue));
}
return new EventPayload(membersNames, memebersValues);
}
return null;
}
public override void WriteObjectData(
TraceLoggingDataCollector collector,
object valueObj)
{
if (this.accessors != null)
{
var value = valueObj == null
? default(ContainerType)
: (ContainerType)valueObj;
this.WriteData(collector, ref value);
}
}
}
}

View File

@@ -0,0 +1,75 @@
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;
}
}
}

View File

@@ -0,0 +1,154 @@
using System;
using System.Reflection;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: Each PropertyAccessor instance encapsulates the information
/// needed to read a particular property from an instance of ContainerType
/// and write the value to a DataCollector. Used by InvokeTypeInfo.
/// </summary>
/// <typeparam name="ContainerType">
/// The type of the object from which properties are read.
/// </typeparam>
internal abstract class PropertyAccessor<ContainerType>
{
public abstract void Write(TraceLoggingDataCollector collector, ref ContainerType value);
public abstract object GetData(ContainerType value);
public static PropertyAccessor<ContainerType> Create(PropertyAnalysis property)
{
// Due to current Project N limitations on handling generic instantiations with
// 2 generic parameters we have to explicitly create the instantiations that we consider
// important to EventSource performance (we have considered int, long, string for the moment).
// Everything else is handled by NonGenericPropertyWriter that ends up boxing the container object.
var retType = property.getterInfo.ReturnType;
if (!Statics.IsValueType(typeof(ContainerType)))
{
if (retType == typeof(int))
return new ClassPropertyWriter<ContainerType, int>(property);
else if (retType == typeof(long))
return new ClassPropertyWriter<ContainerType, long>(property);
else if (retType == typeof(string))
return new ClassPropertyWriter<ContainerType, string>(property);
}
else
{
// Handle the case if it is a struct (DD 1027919)
}
// Otherwise use the boxing one.
return new NonGenericProperytWriter<ContainerType>(property);
}
}
/// <summary>
/// The type specific version of the property writers uses generics in a way
/// that Project N can't handle at the moment. To avoid this we simply
/// use reflection completely.
/// </summary>
internal class NonGenericProperytWriter<ContainerType> : PropertyAccessor<ContainerType>
{
public NonGenericProperytWriter(PropertyAnalysis property)
{
getterInfo = property.getterInfo;
typeInfo = property.typeInfo;
}
public override void Write(TraceLoggingDataCollector collector, ref ContainerType container)
{
object value = container == null
? null
: getterInfo.Invoke((object)container, null);
this.typeInfo.WriteObjectData(collector, value);
}
public override object GetData(ContainerType container)
{
return container == null
? default(ValueType)
: getterInfo.Invoke((object)container, null);
}
private readonly TraceLoggingTypeInfo typeInfo;
private readonly MethodInfo getterInfo;
}
/// <summary>
/// Implementation of PropertyAccessor for use when ContainerType is a
/// value type.
/// </summary>
/// <typeparam name="ContainerType">The type of the object from which properties are read.</typeparam>
/// <typeparam name="ValueType">Type of the property being read.</typeparam>
internal class StructPropertyWriter<ContainerType, ValueType>
: PropertyAccessor<ContainerType>
{
private delegate ValueType Getter(ref ContainerType container);
private readonly TraceLoggingTypeInfo<ValueType> valueTypeInfo;
private readonly Getter getter;
public StructPropertyWriter(PropertyAnalysis property)
{
this.valueTypeInfo = (TraceLoggingTypeInfo<ValueType>)property.typeInfo;
this.getter = (Getter)Statics.CreateDelegate(
typeof(Getter),
property.getterInfo);
}
public override void Write(TraceLoggingDataCollector collector, ref ContainerType container)
{
var value = container == null
? default(ValueType)
: getter(ref container);
this.valueTypeInfo.WriteData(collector, ref value);
}
public override object GetData(ContainerType container)
{
return container == null
? default(ValueType)
: getter(ref container);
}
}
/// <summary>
/// Implementation of PropertyAccessor for use when ContainerType is a
/// reference type.
/// </summary>
/// <typeparam name="ContainerType">The type of the object from which properties are read.</typeparam>
/// <typeparam name="ValueType">Type of the property being read.</typeparam>
internal class ClassPropertyWriter<ContainerType, ValueType>
: PropertyAccessor<ContainerType>
{
private delegate ValueType Getter(ContainerType container);
private readonly TraceLoggingTypeInfo<ValueType> valueTypeInfo;
private readonly Getter getter;
public ClassPropertyWriter(PropertyAnalysis property)
{
this.valueTypeInfo = (TraceLoggingTypeInfo<ValueType>)property.typeInfo;
this.getter = (Getter)Statics.CreateDelegate(
typeof(Getter),
property.getterInfo);
}
public override void Write(TraceLoggingDataCollector collector, ref ContainerType container)
{
var value = container == null
? default(ValueType)
: getter(container);
this.valueTypeInfo.WriteData(collector, ref value);
}
public override object GetData(ContainerType container)
{
return container == null
? default(ValueType)
: getter(container);
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Reflection;
#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: stores the per-property information obtained by
/// reflecting over a type.
/// </summary>
internal sealed class PropertyAnalysis
{
internal readonly string name;
internal readonly MethodInfo getterInfo;
internal readonly TraceLoggingTypeInfo typeInfo;
internal readonly EventFieldAttribute fieldAttribute;
public PropertyAnalysis(
string name,
MethodInfo getterInfo,
TraceLoggingTypeInfo typeInfo,
EventFieldAttribute fieldAttribute)
{
this.name = name;
this.getterInfo = getterInfo;
this.typeInfo = typeInfo;
this.fieldAttribute = fieldAttribute;
}
}
}

Some files were not shown because too many files have changed in this diff Show More