summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Diagnostics
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/mscorlib/src/System/Diagnostics
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/mscorlib/src/System/Diagnostics')
-rw-r--r--src/mscorlib/src/System/Diagnostics/Assert.cs114
-rw-r--r--src/mscorlib/src/System/Diagnostics/AssertFilter.cs46
-rw-r--r--src/mscorlib/src/System/Diagnostics/AssertFilters.cs24
-rw-r--r--src/mscorlib/src/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs76
-rw-r--r--src/mscorlib/src/System/Diagnostics/ConditionalAttribute.cs26
-rw-r--r--src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs1017
-rw-r--r--src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs521
-rw-r--r--src/mscorlib/src/System/Diagnostics/Debugger.cs195
-rw-r--r--src/mscorlib/src/System/Diagnostics/DebuggerAttributes.cs378
-rw-r--r--src/mscorlib/src/System/Diagnostics/EditAndContinueHelper.cs27
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/ActivityTracker.cs670
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventActivityOptions.cs39
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs436
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs193
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs1220
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs6962
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventSourceException.cs53
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs224
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/FrameworkEventSource.cs620
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs373
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs63
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs127
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs25
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs319
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs17
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs30
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs64
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs146
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs76
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs130
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs25
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs154
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceActivity.cs321
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventSourceOptions.cs130
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/FieldMetadata.cs231
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/InvokeTypeInfo.cs96
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/NameInfo.cs79
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyAnalysis.cs39
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/PropertyValue.cs251
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleEventTypes.cs39
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/SimpleTypeInfos.cs296
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/Statics.cs727
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataCollector.cs105
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingDataType.cs349
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventSource.cs902
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTraits.cs28
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingEventTypes.cs262
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingMetadataCollector.cs370
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TraceLoggingTypeInfo.cs209
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/TypeAnalysis.cs103
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/Winmeta.cs196
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/XplatEventLogger.cs164
-rw-r--r--src/mscorlib/src/System/Diagnostics/ICustomDebuggerNotification.cs21
-rw-r--r--src/mscorlib/src/System/Diagnostics/LogSwitch.cs142
-rw-r--r--src/mscorlib/src/System/Diagnostics/LoggingLevels.cs45
-rw-r--r--src/mscorlib/src/System/Diagnostics/Stackframe.cs348
-rw-r--r--src/mscorlib/src/System/Diagnostics/Stacktrace.cs770
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymBinder.cs42
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymDocument.cs48
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymDocumentWriter.cs31
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymMethod.cs86
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymNamespace.cs30
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymReader.cs68
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymScope.cs47
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymVariable.cs41
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/ISymWriter.cs218
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/SymAddressKind.cs52
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/SymDocumentType.cs24
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/SymLanguageType.cs42
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/SymLanguageVendor.cs23
-rw-r--r--src/mscorlib/src/System/Diagnostics/SymbolStore/Token.cs51
-rw-r--r--src/mscorlib/src/System/Diagnostics/log.cs251
72 files changed, 21667 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Assert.cs b/src/mscorlib/src/System/Diagnostics/Assert.cs
new file mode 100644
index 0000000000..40efe753af
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Assert.cs
@@ -0,0 +1,114 @@
+// 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.
+
+namespace System.Diagnostics {
+ using System;
+ using System.Security.Permissions;
+ using System.IO;
+ using System.Reflection;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+
+ // Class which handles code asserts. Asserts are used to explicitly protect
+ // assumptions made in the code. In general if an assert fails, it indicates
+ // a program bug so is immediately called to the attention of the user.
+ // Only static data members, does not need to be marked with the serializable attribute
+ internal static class Assert
+ {
+ internal const int COR_E_FAILFAST = unchecked((int) 0x80131623);
+ private static AssertFilter Filter;
+
+ static Assert()
+ {
+ Filter = new DefaultFilter();
+ }
+
+ // Called when an assertion is being made.
+ //
+ internal static void Check(bool condition, String conditionString, String message)
+ {
+ if (!condition)
+ {
+ Fail (conditionString, message, null, COR_E_FAILFAST);
+ }
+ }
+
+ internal static void Check(bool condition, String conditionString, String message, int exitCode)
+ {
+ if (!condition)
+ {
+ Fail(conditionString, message, null, exitCode);
+ }
+ }
+
+ internal static void Fail(String conditionString, String message)
+ {
+ Fail(conditionString, message, null, COR_E_FAILFAST);
+ }
+
+ internal static void Fail(String conditionString, String message, String windowTitle, int exitCode)
+ {
+ Fail(conditionString, message, windowTitle, exitCode, StackTrace.TraceFormat.Normal, 0);
+ }
+
+ internal static void Fail(String conditionString, String message, int exitCode, StackTrace.TraceFormat stackTraceFormat)
+ {
+ Fail(conditionString, message, null, exitCode, stackTraceFormat, 0);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ internal static void Fail(String conditionString, String message, String windowTitle, int exitCode, StackTrace.TraceFormat stackTraceFormat, int numStackFramesToSkip)
+ {
+ // get the stacktrace
+ StackTrace st = new StackTrace(numStackFramesToSkip, true);
+
+ AssertFilters iResult = Filter.AssertFailure (conditionString, message, st, stackTraceFormat, windowTitle);
+
+ if (iResult == AssertFilters.FailDebug)
+ {
+ if (Debugger.IsAttached == true)
+ Debugger.Break();
+ else
+ {
+ if (Debugger.Launch() == false)
+ {
+ throw new InvalidOperationException(
+ Environment.GetResourceString("InvalidOperation_DebuggerLaunchFailed"));
+ }
+ }
+ }
+ else if (iResult == AssertFilters.FailTerminate)
+ {
+#if FEATURE_CORECLR
+ // We want to exit the Silverlight application, after displaying a message.
+ // Our best known way to emulate this is to exit the process with a known
+ // error code. Jolt may not be prepared for an appdomain to be unloaded.
+ Environment._Exit(exitCode);
+#else
+ // This assert dialog will be common for code contract failures. If a code contract failure
+ // occurs on an end user machine, we believe the right experience is to do a FailFast, which
+ // will report this error via Watson, so someone could theoretically fix the bug.
+ // However, in CLR v4, Environment.FailFast when a debugger is attached gives you an MDA
+ // saying you've hit a bug in the runtime or unsafe managed code, and this is most likely caused
+ // by heap corruption or a stack imbalance from COM Interop or P/Invoke. That extremely
+ // misleading error isn't right, and we can temporarily work around this by using Environment.Exit
+ // if a debugger is attached. The right fix is to plumb FailFast correctly through our native
+ // Watson code, adding in a TypeOfReportedError for fatal managed errors.
+ if (Debugger.IsAttached)
+ Environment._Exit(exitCode);
+ else
+ Environment.FailFast(message, unchecked((uint) exitCode));
+#endif
+ }
+ }
+
+ // Called when an assert happens.
+ // windowTitle can be null.
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern static int ShowDefaultAssertDialog(String conditionString, String message, String stackTrace, String windowTitle);
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/AssertFilter.cs b/src/mscorlib/src/System/Diagnostics/AssertFilter.cs
new file mode 100644
index 0000000000..b441fc326b
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/AssertFilter.cs
@@ -0,0 +1,46 @@
+// 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.
+
+namespace System.Diagnostics {
+
+
+ using System;
+ using System.Runtime.Versioning;
+ // A Filter is used to decide whether an assert failure
+ // should terminate the program (or invoke the debugger).
+ // Typically this is done by popping up a dialog & asking the user.
+ //
+ // The default filter brings up a simple Win32 dialog with 3 buttons.
+
+ [Serializable]
+ abstract internal class AssertFilter
+ {
+
+ // Called when an assert fails. This should be overridden with logic which
+ // determines whether the program should terminate or not. Typically this
+ // is done by asking the user.
+ //
+ // The windowTitle can be null.
+ abstract public AssertFilters AssertFailure(String condition, String message,
+ StackTrace location, StackTrace.TraceFormat stackTraceFormat, String windowTitle);
+
+ }
+ // No data, does not need to be marked with the serializable attribute
+ internal class DefaultFilter : AssertFilter
+ {
+ internal DefaultFilter()
+ {
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public override AssertFilters AssertFailure(String condition, String message,
+ StackTrace location, StackTrace.TraceFormat stackTraceFormat,
+ String windowTitle)
+
+ {
+ return (AssertFilters) Assert.ShowDefaultAssertDialog (condition, message, location.ToString(stackTraceFormat), windowTitle);
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Diagnostics/AssertFilters.cs b/src/mscorlib/src/System/Diagnostics/AssertFilters.cs
new file mode 100644
index 0000000000..13131d4819
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/AssertFilters.cs
@@ -0,0 +1,24 @@
+// 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.
+
+namespace System.Diagnostics {
+
+ /*
+ * FailDebug indicates the debugger should be invoked
+ * FailIgnore indicates the failure should be ignored & the
+ * program continued
+ * FailTerminate indicates that the program should be terminated
+ * FailContinue indicates that no decision is made -
+ * the previous Filter should be invoked
+ */
+ using System;
+ [Serializable]
+ internal enum AssertFilters
+ {
+ FailDebug = 0,
+ FailIgnore = 1,
+ FailTerminate = 2,
+ FailContinueFilter = 3,
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs b/src/mscorlib/src/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs
new file mode 100644
index 0000000000..fb26fcc4ab
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.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.
+
+/*============================================================
+**
+**
+**
+** An attribute to suppress violation messages/warnings
+** by static code analysis tools.
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System.Diagnostics.CodeAnalysis
+{
+
+ [AttributeUsage(
+ AttributeTargets.All,
+ Inherited = false,
+ AllowMultiple = true
+ )
+ ]
+ [Conditional("CODE_ANALYSIS")]
+ public sealed class SuppressMessageAttribute : Attribute
+ {
+ private string category;
+ private string justification;
+ private string checkId;
+ private string scope;
+ private string target;
+ private string messageId;
+
+ public SuppressMessageAttribute(string category, string checkId)
+ {
+ this.category = category;
+ this.checkId = checkId;
+ }
+
+ public string Category
+ {
+ get { return category; }
+ }
+
+ public string CheckId
+ {
+ get { return checkId; }
+ }
+
+ public string Scope
+ {
+ get { return scope; }
+ set { scope = value; }
+ }
+
+ public string Target
+ {
+ get { return target; }
+ set { target = value; }
+ }
+
+ public string MessageId
+ {
+ get { return messageId; }
+ set { messageId = value; }
+ }
+
+ public string Justification
+ {
+ get { return justification; }
+ set { justification = value; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/ConditionalAttribute.cs b/src/mscorlib/src/System/Diagnostics/ConditionalAttribute.cs
new file mode 100644
index 0000000000..b20931f9c3
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/ConditionalAttribute.cs
@@ -0,0 +1,26 @@
+// 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;
+
+namespace System.Diagnostics {
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=true)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class ConditionalAttribute : Attribute
+ {
+ public ConditionalAttribute(String conditionString)
+ {
+ m_conditionString = conditionString;
+ }
+
+ public String ConditionString {
+ get {
+ return m_conditionString;
+ }
+ }
+
+ private String m_conditionString;
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs b/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs
new file mode 100644
index 0000000000..e002e6a5c1
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs
@@ -0,0 +1,1017 @@
+// 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.
+
+/*============================================================
+**
+**
+**
+** Purpose: The contract class allows for expressing preconditions,
+** postconditions, and object invariants about methods in source
+** code for runtime checking & static analysis.
+**
+** Two classes (Contract and ContractHelper) are split into partial classes
+** in order to share the public front for multiple platforms (this file)
+** while providing separate implementation details for each platform.
+**
+===========================================================*/
+#define DEBUG // The behavior of this contract library should be consistent regardless of build type.
+
+#if SILVERLIGHT
+#define FEATURE_UNTRUSTED_CALLERS
+
+#elif REDHAWK_RUNTIME
+
+#elif BARTOK_RUNTIME
+
+#else // CLR
+#define FEATURE_UNTRUSTED_CALLERS
+#define FEATURE_RELIABILITY_CONTRACTS
+#define FEATURE_SERIALIZATION
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+#if FEATURE_RELIABILITY_CONTRACTS
+using System.Runtime.ConstrainedExecution;
+#endif
+#if FEATURE_UNTRUSTED_CALLERS
+using System.Security;
+using System.Security.Permissions;
+#endif
+
+namespace System.Diagnostics.Contracts {
+
+ #region Attributes
+
+ /// <summary>
+ /// Methods and classes marked with this attribute can be used within calls to Contract methods. Such methods not make any visible state changes.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Delegate | AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public sealed class PureAttribute : Attribute
+ {
+ }
+
+ /// <summary>
+ /// Types marked with this attribute specify that a separate type contains the contracts for this type.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [Conditional("DEBUG")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
+ public sealed class ContractClassAttribute : Attribute
+ {
+ private Type _typeWithContracts;
+
+ public ContractClassAttribute(Type typeContainingContracts)
+ {
+ _typeWithContracts = typeContainingContracts;
+ }
+
+ public Type TypeContainingContracts {
+ get { return _typeWithContracts; }
+ }
+ }
+
+ /// <summary>
+ /// Types marked with this attribute specify that they are a contract for the type that is the argument of the constructor.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public sealed class ContractClassForAttribute : Attribute
+ {
+ private Type _typeIAmAContractFor;
+
+ public ContractClassForAttribute(Type typeContractsAreFor)
+ {
+ _typeIAmAContractFor = typeContractsAreFor;
+ }
+
+ public Type TypeContractsAreFor {
+ get { return _typeIAmAContractFor; }
+ }
+ }
+
+ /// <summary>
+ /// This attribute is used to mark a method as being the invariant
+ /// method for a class. The method can have any name, but it must
+ /// return "void" and take no parameters. The body of the method
+ /// must consist solely of one or more calls to the method
+ /// Contract.Invariant. A suggested name for the method is
+ /// "ObjectInvariant".
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+ public sealed class ContractInvariantMethodAttribute : Attribute
+ {
+ }
+
+ /// <summary>
+ /// Attribute that specifies that an assembly is a reference assembly with contracts.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Assembly)]
+ public sealed class ContractReferenceAssemblyAttribute : Attribute
+ {
+ }
+
+ /// <summary>
+ /// Methods (and properties) marked with this attribute can be used within calls to Contract methods, but have no runtime behavior associated with them.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
+ public sealed class ContractRuntimeIgnoredAttribute : Attribute
+ {
+ }
+
+ /*
+ [Serializable]
+ internal enum Mutability
+ {
+ Immutable, // read-only after construction, except for lazy initialization & caches
+ // Do we need a "deeply immutable" value?
+ Mutable,
+ HasInitializationPhase, // read-only after some point.
+ // Do we need a value for mutable types with read-only wrapper subclasses?
+ }
+
+ // Note: This hasn't been thought through in any depth yet. Consider it experimental.
+ // We should replace this with Joe's concepts.
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
+ [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "Thank you very much, but we like the names we've defined for the accessors")]
+ internal sealed class MutabilityAttribute : Attribute
+ {
+ private Mutability _mutabilityMarker;
+
+ public MutabilityAttribute(Mutability mutabilityMarker)
+ {
+ _mutabilityMarker = mutabilityMarker;
+ }
+
+ public Mutability Mutability {
+ get { return _mutabilityMarker; }
+ }
+ }
+ */
+
+ /// <summary>
+ /// Instructs downstream tools whether to assume the correctness of this assembly, type or member without performing any verification or not.
+ /// Can use [ContractVerification(false)] to explicitly mark assembly, type or member as one to *not* have verification performed on it.
+ /// Most specific element found (member, type, then assembly) takes precedence.
+ /// (That is useful if downstream tools allow a user to decide which polarity is the default, unmarked case.)
+ /// </summary>
+ /// <remarks>
+ /// Apply this attribute to a type to apply to all members of the type, including nested types.
+ /// Apply this attribute to an assembly to apply to all types and members of the assembly.
+ /// Apply this attribute to a property to apply to both the getter and setter.
+ /// </remarks>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]
+ public sealed class ContractVerificationAttribute : Attribute
+ {
+ private bool _value;
+
+ public ContractVerificationAttribute(bool value) { _value = value; }
+
+ public bool Value {
+ get { return _value; }
+ }
+ }
+
+ /// <summary>
+ /// Allows a field f to be used in the method contracts for a method m when f has less visibility than m.
+ /// For instance, if the method is public, but the field is private.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+ [AttributeUsage(AttributeTargets.Field)]
+ [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "Thank you very much, but we like the names we've defined for the accessors")]
+ public sealed class ContractPublicPropertyNameAttribute : Attribute
+ {
+ private String _publicName;
+
+ public ContractPublicPropertyNameAttribute(String name)
+ {
+ _publicName = name;
+ }
+
+ public String Name {
+ get { return _publicName; }
+ }
+ }
+
+ /// <summary>
+ /// Enables factoring legacy if-then-throw into separate methods for reuse and full control over
+ /// thrown exception and arguments
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ [Conditional("CONTRACTS_FULL")]
+ public sealed class ContractArgumentValidatorAttribute : Attribute
+ {
+ }
+
+ /// <summary>
+ /// Enables writing abbreviations for contracts that get copied to other methods
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ [Conditional("CONTRACTS_FULL")]
+ public sealed class ContractAbbreviatorAttribute : Attribute
+ {
+ }
+
+ /// <summary>
+ /// Allows setting contract and tool options at assembly, type, or method granularity.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+ [Conditional("CONTRACTS_FULL")]
+ public sealed class ContractOptionAttribute : Attribute
+ {
+ private String _category;
+ private String _setting;
+ private bool _enabled;
+ private String _value;
+
+ public ContractOptionAttribute(String category, String setting, bool enabled)
+ {
+ _category = category;
+ _setting = setting;
+ _enabled = enabled;
+ }
+
+ public ContractOptionAttribute(String category, String setting, String value)
+ {
+ _category = category;
+ _setting = setting;
+ _value = value;
+ }
+
+ public String Category {
+ get { return _category; }
+ }
+
+ public String Setting {
+ get { return _setting; }
+ }
+
+ public bool Enabled {
+ get { return _enabled; }
+ }
+
+ public String Value {
+ get { return _value; }
+ }
+ }
+
+ #endregion Attributes
+
+ /// <summary>
+ /// Contains static methods for representing program contracts such as preconditions, postconditions, and invariants.
+ /// </summary>
+ /// <remarks>
+ /// WARNING: A binary rewriter must be used to insert runtime enforcement of these contracts.
+ /// Otherwise some contracts like Ensures can only be checked statically and will not throw exceptions during runtime when contracts are violated.
+ /// Please note this class uses conditional compilation to help avoid easy mistakes. Defining the preprocessor
+ /// symbol CONTRACTS_PRECONDITIONS will include all preconditions expressed using Contract.Requires in your
+ /// build. The symbol CONTRACTS_FULL will include postconditions and object invariants, and requires the binary rewriter.
+ /// </remarks>
+ public static partial class Contract
+ {
+ #region User Methods
+
+ #region Assume
+
+ /// <summary>
+ /// Instructs code analysis tools to assume the expression <paramref name="condition"/> is true even if it can not be statically proven to always be true.
+ /// </summary>
+ /// <param name="condition">Expression to assume will always be true.</param>
+ /// <remarks>
+ /// At runtime this is equivalent to an <seealso cref="System.Diagnostics.Contracts.Contract.Assert(bool)"/>.
+ /// </remarks>
+ [Pure]
+ [Conditional("DEBUG")]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Assume(bool condition)
+ {
+ if (!condition) {
+ ReportFailure(ContractFailureKind.Assume, null, null, null);
+ }
+ }
+
+ /// <summary>
+ /// Instructs code analysis tools to assume the expression <paramref name="condition"/> is true even if it can not be statically proven to always be true.
+ /// </summary>
+ /// <param name="condition">Expression to assume will always be true.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// At runtime this is equivalent to an <seealso cref="System.Diagnostics.Contracts.Contract.Assert(bool)"/>.
+ /// </remarks>
+ [Pure]
+ [Conditional("DEBUG")]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Assume(bool condition, String userMessage)
+ {
+ if (!condition) {
+ ReportFailure(ContractFailureKind.Assume, userMessage, null, null);
+ }
+ }
+
+ #endregion Assume
+
+ #region Assert
+
+ /// <summary>
+ /// In debug builds, perform a runtime check that <paramref name="condition"/> is true.
+ /// </summary>
+ /// <param name="condition">Expression to check to always be true.</param>
+ [Pure]
+ [Conditional("DEBUG")]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Assert(bool condition)
+ {
+ if (!condition)
+ ReportFailure(ContractFailureKind.Assert, null, null, null);
+ }
+
+ /// <summary>
+ /// In debug builds, perform a runtime check that <paramref name="condition"/> is true.
+ /// </summary>
+ /// <param name="condition">Expression to check to always be true.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ [Pure]
+ [Conditional("DEBUG")]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Assert(bool condition, String userMessage)
+ {
+ if (!condition)
+ ReportFailure(ContractFailureKind.Assert, userMessage, null, null);
+ }
+
+ #endregion Assert
+
+ #region Requires
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> must be true before the enclosing method or property is invoked.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// Use this form when backward compatibility does not force you to throw a particular exception.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Requires(bool condition)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> must be true before the enclosing method or property is invoked.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// Use this form when backward compatibility does not force you to throw a particular exception.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Requires(bool condition, String userMessage)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> must be true before the enclosing method or property is invoked.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// Use this form when you want to throw a particular exception.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "condition")]
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Requires<TException>(bool condition) where TException : Exception
+ {
+ AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires<TException>");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> must be true before the enclosing method or property is invoked.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// Use this form when you want to throw a particular exception.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "userMessage")]
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "condition")]
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Requires<TException>(bool condition, String userMessage) where TException : Exception
+ {
+ AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires<TException>");
+ }
+
+ #endregion Requires
+
+ #region Ensures
+
+ /// <summary>
+ /// Specifies a public contract such that the expression <paramref name="condition"/> will be true when the enclosing method or property returns normally.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract. May include <seealso cref="OldValue"/> and <seealso cref="Result"/>.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this postcondition.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Ensures(bool condition)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Postcondition, "Ensures");
+ }
+
+ /// <summary>
+ /// Specifies a public contract such that the expression <paramref name="condition"/> will be true when the enclosing method or property returns normally.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract. May include <seealso cref="OldValue"/> and <seealso cref="Result"/>.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference members at least as visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this postcondition.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Ensures(bool condition, String userMessage)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Postcondition, "Ensures");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that if an exception of type <typeparamref name="TException"/> is thrown then the expression <paramref name="condition"/> will be true when the enclosing method or property terminates abnormally.
+ /// </summary>
+ /// <typeparam name="TException">Type of exception related to this postcondition.</typeparam>
+ /// <param name="condition">Boolean expression representing the contract. May include <seealso cref="OldValue"/> and <seealso cref="Result"/>.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference types and members at least as visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this postcondition.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Exception type used in tools.")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void EnsuresOnThrow<TException>(bool condition) where TException : Exception
+ {
+ AssertMustUseRewriter(ContractFailureKind.PostconditionOnException, "EnsuresOnThrow");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that if an exception of type <typeparamref name="TException"/> is thrown then the expression <paramref name="condition"/> will be true when the enclosing method or property terminates abnormally.
+ /// </summary>
+ /// <typeparam name="TException">Type of exception related to this postcondition.</typeparam>
+ /// <param name="condition">Boolean expression representing the contract. May include <seealso cref="OldValue"/> and <seealso cref="Result"/>.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// This call must happen at the beginning of a method or property before any other code.
+ /// This contract is exposed to clients so must only reference types and members at least as visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this postcondition.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Exception type used in tools.")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void EnsuresOnThrow<TException>(bool condition, String userMessage) where TException : Exception
+ {
+ AssertMustUseRewriter(ContractFailureKind.PostconditionOnException, "EnsuresOnThrow");
+ }
+
+ #region Old, Result, and Out Parameters
+
+ /// <summary>
+ /// Represents the result (a.k.a. return value) of a method or property.
+ /// </summary>
+ /// <typeparam name="T">Type of return value of the enclosing method or property.</typeparam>
+ /// <returns>Return value of the enclosing method or property.</returns>
+ /// <remarks>
+ /// This method can only be used within the argument to the <seealso cref="Ensures(bool)"/> contract.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Not intended to be called at runtime.")]
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static T Result<T>() { return default(T); }
+
+ /// <summary>
+ /// Represents the final (output) value of an out parameter when returning from a method.
+ /// </summary>
+ /// <typeparam name="T">Type of the out parameter.</typeparam>
+ /// <param name="value">The out parameter.</param>
+ /// <returns>The output value of the out parameter.</returns>
+ /// <remarks>
+ /// This method can only be used within the argument to the <seealso cref="Ensures(bool)"/> contract.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Not intended to be called at runtime.")]
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static T ValueAtReturn<T>(out T value) { value = default(T); return value; }
+
+ /// <summary>
+ /// Represents the value of <paramref name="value"/> as it was at the start of the method or property.
+ /// </summary>
+ /// <typeparam name="T">Type of <paramref name="value"/>. This can be inferred.</typeparam>
+ /// <param name="value">Value to represent. This must be a field or parameter.</param>
+ /// <returns>Value of <paramref name="value"/> at the start of the method or property.</returns>
+ /// <remarks>
+ /// This method can only be used within the argument to the <seealso cref="Ensures(bool)"/> contract.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value")]
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static T OldValue<T>(T value) { return default(T); }
+
+ #endregion Old, Result, and Out Parameters
+
+ #endregion Ensures
+
+ #region Invariant
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> will be true after every method or property on the enclosing class.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <remarks>
+ /// This contact can only be specified in a dedicated invariant method declared on a class.
+ /// This contract is not exposed to clients so may reference members less visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this invariant.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Invariant(bool condition)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Invariant, "Invariant");
+ }
+
+ /// <summary>
+ /// Specifies a contract such that the expression <paramref name="condition"/> will be true after every method or property on the enclosing class.
+ /// </summary>
+ /// <param name="condition">Boolean expression representing the contract.</param>
+ /// <param name="userMessage">If it is not a constant string literal, then the contract may not be understood by tools.</param>
+ /// <remarks>
+ /// This contact can only be specified in a dedicated invariant method declared on a class.
+ /// This contract is not exposed to clients so may reference members less visible as the enclosing method.
+ /// The contract rewriter must be used for runtime enforcement of this invariant.
+ /// </remarks>
+ [Pure]
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static void Invariant(bool condition, String userMessage)
+ {
+ AssertMustUseRewriter(ContractFailureKind.Invariant, "Invariant");
+ }
+
+ #endregion Invariant
+
+ #region Quantifiers
+
+ #region ForAll
+
+ /// <summary>
+ /// Returns whether the <paramref name="predicate"/> returns <c>true</c>
+ /// for all integers starting from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.
+ /// </summary>
+ /// <param name="fromInclusive">First integer to pass to <paramref name="predicate"/>.</param>
+ /// <param name="toExclusive">One greater than the last integer to pass to <paramref name="predicate"/>.</param>
+ /// <param name="predicate">Function that is evaluated from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.</param>
+ /// <returns><c>true</c> if <paramref name="predicate"/> returns <c>true</c> for all integers
+ /// starting from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.</returns>
+ /// <seealso cref="System.Collections.Generic.List&lt;T&gt;.TrueForAll"/>
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] // Assumes predicate obeys CER rules.
+#endif
+ public static bool ForAll(int fromInclusive, int toExclusive, Predicate<int> predicate)
+ {
+ if (fromInclusive > toExclusive)
+#if INSIDE_CLR
+ throw new ArgumentException(Environment.GetResourceString("Argument_ToExclusiveLessThanFromExclusive"));
+#else
+ throw new ArgumentException("fromInclusive must be less than or equal to toExclusive.");
+#endif
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+ Contract.EndContractBlock();
+
+ for (int i = fromInclusive; i < toExclusive; i++)
+ if (!predicate(i)) return false;
+ return true;
+ }
+
+
+ /// <summary>
+ /// Returns whether the <paramref name="predicate"/> returns <c>true</c>
+ /// for all elements in the <paramref name="collection"/>.
+ /// </summary>
+ /// <param name="collection">The collection from which elements will be drawn from to pass to <paramref name="predicate"/>.</param>
+ /// <param name="predicate">Function that is evaluated on elements from <paramref name="collection"/>.</param>
+ /// <returns><c>true</c> if and only if <paramref name="predicate"/> returns <c>true</c> for all elements in
+ /// <paramref name="collection"/>.</returns>
+ /// <seealso cref="System.Collections.Generic.List&lt;T&gt;.TrueForAll"/>
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] // Assumes predicate & collection enumerator obey CER rules.
+#endif
+ public static bool ForAll<T>(IEnumerable<T> collection, Predicate<T> predicate)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+ Contract.EndContractBlock();
+
+ foreach (T t in collection)
+ if (!predicate(t)) return false;
+ return true;
+ }
+
+ #endregion ForAll
+
+ #region Exists
+
+ /// <summary>
+ /// Returns whether the <paramref name="predicate"/> returns <c>true</c>
+ /// for any integer starting from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.
+ /// </summary>
+ /// <param name="fromInclusive">First integer to pass to <paramref name="predicate"/>.</param>
+ /// <param name="toExclusive">One greater than the last integer to pass to <paramref name="predicate"/>.</param>
+ /// <param name="predicate">Function that is evaluated from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.</param>
+ /// <returns><c>true</c> if <paramref name="predicate"/> returns <c>true</c> for any integer
+ /// starting from <paramref name="fromInclusive"/> to <paramref name="toExclusive"/> - 1.</returns>
+ /// <seealso cref="System.Collections.Generic.List&lt;T&gt;.Exists"/>
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] // Assumes predicate obeys CER rules.
+#endif
+ public static bool Exists(int fromInclusive, int toExclusive, Predicate<int> predicate)
+ {
+ if (fromInclusive > toExclusive)
+#if INSIDE_CLR
+ throw new ArgumentException(Environment.GetResourceString("Argument_ToExclusiveLessThanFromExclusive"));
+#else
+ throw new ArgumentException("fromInclusive must be less than or equal to toExclusive.");
+#endif
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+ Contract.EndContractBlock();
+
+ for (int i = fromInclusive; i < toExclusive; i++)
+ if (predicate(i)) return true;
+ return false;
+ }
+
+ /// <summary>
+ /// Returns whether the <paramref name="predicate"/> returns <c>true</c>
+ /// for any element in the <paramref name="collection"/>.
+ /// </summary>
+ /// <param name="collection">The collection from which elements will be drawn from to pass to <paramref name="predicate"/>.</param>
+ /// <param name="predicate">Function that is evaluated on elements from <paramref name="collection"/>.</param>
+ /// <returns><c>true</c> if and only if <paramref name="predicate"/> returns <c>true</c> for an element in
+ /// <paramref name="collection"/>.</returns>
+ /// <seealso cref="System.Collections.Generic.List&lt;T&gt;.Exists"/>
+ [Pure]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] // Assumes predicate & collection enumerator obey CER rules.
+#endif
+ public static bool Exists<T>(IEnumerable<T> collection, Predicate<T> predicate)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+ Contract.EndContractBlock();
+
+ foreach (T t in collection)
+ if (predicate(t)) return true;
+ return false;
+ }
+
+ #endregion Exists
+
+ #endregion Quantifiers
+
+ #region Pointers
+#if FEATURE_UNSAFE_CONTRACTS
+ /// <summary>
+ /// Runtime checking for pointer bounds is not currently feasible. Thus, at runtime, we just return
+ /// a very long extent for each pointer that is writable. As long as assertions are of the form
+ /// WritableBytes(ptr) >= ..., the runtime assertions will not fail.
+ /// The runtime value is 2^64 - 1 or 2^32 - 1.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1802", Justification = "FxCop is confused")]
+ static readonly ulong MaxWritableExtent = (UIntPtr.Size == 4) ? UInt32.MaxValue : UInt64.MaxValue;
+
+ /// <summary>
+ /// Allows specifying a writable extent for a UIntPtr, similar to SAL's writable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes writable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static ulong WritableBytes(UIntPtr startAddress) { return MaxWritableExtent - startAddress.ToUInt64(); }
+
+ /// <summary>
+ /// Allows specifying a writable extent for a UIntPtr, similar to SAL's writable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes writable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static ulong WritableBytes(IntPtr startAddress) { return MaxWritableExtent - (ulong)startAddress; }
+
+ /// <summary>
+ /// Allows specifying a writable extent for a UIntPtr, similar to SAL's writable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes writable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+ [SecurityCritical]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+
+ unsafe public static ulong WritableBytes(void* startAddress) { return MaxWritableExtent - (ulong)startAddress; }
+
+ /// <summary>
+ /// Allows specifying a readable extent for a UIntPtr, similar to SAL's readable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes readable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static ulong ReadableBytes(UIntPtr startAddress) { return MaxWritableExtent - startAddress.ToUInt64(); }
+
+ /// <summary>
+ /// Allows specifying a readable extent for a UIntPtr, similar to SAL's readable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes readable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static ulong ReadableBytes(IntPtr startAddress) { return MaxWritableExtent - (ulong)startAddress; }
+
+ /// <summary>
+ /// Allows specifying a readable extent for a UIntPtr, similar to SAL's readable extent.
+ /// NOTE: this is for static checking only. No useful runtime code can be generated for this
+ /// at the moment.
+ /// </summary>
+ /// <param name="startAddress">Start of memory region</param>
+ /// <returns>The result is the number of bytes readable starting at <paramref name="startAddress"/></returns>
+ [CLSCompliant(false)]
+ [Pure]
+ [ContractRuntimeIgnored]
+ [SecurityCritical]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ unsafe public static ulong ReadableBytes(void* startAddress) { return MaxWritableExtent - (ulong)startAddress; }
+#endif // FEATURE_UNSAFE_CONTRACTS
+ #endregion
+
+ #region Misc.
+
+ /// <summary>
+ /// Marker to indicate the end of the contract section of a method.
+ /// </summary>
+ [Conditional("CONTRACTS_FULL")]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static void EndContractBlock() { }
+
+ #endregion
+
+ #endregion User Methods
+
+ #region Failure Behavior
+
+ /// <summary>
+ /// Without contract rewriting, failing Assert/Assumes end up calling this method.
+ /// Code going through the contract rewriter never calls this method. Instead, the rewriter produced failures call
+ /// System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent, followed by
+ /// System.Runtime.CompilerServices.ContractHelper.TriggerFailure.
+ /// </summary>
+ static partial void ReportFailure(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException);
+
+ /// <summary>
+ /// This method is used internally to trigger a failure indicating to the "programmer" that he is using the interface incorrectly.
+ /// It is NEVER used to indicate failure of actual contracts at runtime.
+ /// </summary>
+ static partial void AssertMustUseRewriter(ContractFailureKind kind, String contractKind);
+
+ #endregion
+ }
+
+ public enum ContractFailureKind {
+ Precondition,
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
+ Postcondition,
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
+ PostconditionOnException,
+ Invariant,
+ Assert,
+ Assume,
+ }
+
+
+}
+
+// Note: In .NET FX 4.5, we duplicated the ContractHelper class in the System.Runtime.CompilerServices
+// namespace to remove an ugly wart of a namespace from the Windows 8 profile. But we still need the
+// old locations left around, so we can support rewritten .NET FX 4.0 libraries. Consider removing
+// these from our reference assembly in a future version.
+namespace System.Diagnostics.Contracts.Internal
+{
+ [Obsolete("Use the ContractHelper class in the System.Runtime.CompilerServices namespace instead.")]
+ public static class ContractHelper
+ {
+ #region Rewriter Failure Hooks
+
+ /// <summary>
+ /// Rewriter will call this method on a contract failure to allow listeners to be notified.
+ /// The method should not perform any failure (assert/throw) itself.
+ /// </summary>
+ /// <returns>null if the event was handled and should not trigger a failure.
+ /// Otherwise, returns the localized failure message</returns>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static string RaiseContractFailedEvent(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException)
+ {
+ return System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent(failureKind, userMessage, conditionText, innerException);
+ }
+
+ /// <summary>
+ /// Rewriter calls this method to get the default failure behavior.
+ /// </summary>
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static void TriggerFailure(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException)
+ {
+ System.Runtime.CompilerServices.ContractHelper.TriggerFailure(kind, displayMessage, userMessage, conditionText, innerException);
+ }
+
+ #endregion Rewriter Failure Hooks
+ }
+} // namespace System.Diagnostics.Contracts.Internal
+
+
+namespace System.Runtime.CompilerServices
+{
+ public static partial class ContractHelper
+ {
+ #region Rewriter Failure Hooks
+
+ /// <summary>
+ /// Rewriter will call this method on a contract failure to allow listeners to be notified.
+ /// The method should not perform any failure (assert/throw) itself.
+ /// </summary>
+ /// <returns>null if the event was handled and should not trigger a failure.
+ /// Otherwise, returns the localized failure message</returns>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public static string RaiseContractFailedEvent(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException)
+ {
+ var resultFailureMessage = "Contract failed"; // default in case implementation does not assign anything.
+ RaiseContractFailedEventImplementation(failureKind, userMessage, conditionText, innerException, ref resultFailureMessage);
+ return resultFailureMessage;
+ }
+
+
+ /// <summary>
+ /// Rewriter calls this method to get the default failure behavior.
+ /// </summary>
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+#endif
+ public static void TriggerFailure(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException)
+ {
+ TriggerFailureImplementation(kind, displayMessage, userMessage, conditionText, innerException);
+ }
+
+ #endregion Rewriter Failure Hooks
+
+ #region Implementation Stubs
+
+ /// <summary>
+ /// Rewriter will call this method on a contract failure to allow listeners to be notified.
+ /// The method should not perform any failure (assert/throw) itself.
+ /// This method has 3 functions:
+ /// 1. Call any contract hooks (such as listeners to Contract failed events)
+ /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null)
+ /// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently.
+ /// </summary>
+ /// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough.
+ /// On exit: null if the event was handled and should not trigger a failure.
+ /// Otherwise, returns the localized failure message</param>
+ static partial void RaiseContractFailedEventImplementation(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException, ref string resultFailureMessage);
+
+ /// <summary>
+ /// Implements the default failure behavior of the platform. Under the BCL, it triggers an Assert box.
+ /// </summary>
+ static partial void TriggerFailureImplementation(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException);
+
+ #endregion Implementation Stubs
+ }
+} // namespace System.Runtime.CompilerServices
+
diff --git a/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
new file mode 100644
index 0000000000..804318e702
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
@@ -0,0 +1,521 @@
+// 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.
+
+/*============================================================
+**
+**
+**
+** Implementation details of CLR Contracts.
+**
+===========================================================*/
+#define DEBUG // The behavior of this contract library should be consistent regardless of build type.
+
+#if SILVERLIGHT
+#define FEATURE_UNTRUSTED_CALLERS
+#elif REDHAWK_RUNTIME
+
+#elif BARTOK_RUNTIME
+
+#else // CLR
+#define FEATURE_UNTRUSTED_CALLERS
+#define FEATURE_RELIABILITY_CONTRACTS
+#define FEATURE_SERIALIZATION
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Reflection;
+
+#if FEATURE_RELIABILITY_CONTRACTS
+using System.Runtime.ConstrainedExecution;
+#endif
+#if FEATURE_UNTRUSTED_CALLERS
+using System.Security;
+using System.Security.Permissions;
+#endif
+
+namespace System.Diagnostics.Contracts {
+
+ public static partial class Contract
+ {
+ #region Private Methods
+
+ [ThreadStatic]
+ private static bool _assertingMustUseRewriter;
+
+ /// <summary>
+ /// This method is used internally to trigger a failure indicating to the "programmer" that he is using the interface incorrectly.
+ /// It is NEVER used to indicate failure of actual contracts at runtime.
+ /// </summary>
+ [SecuritySafeCritical]
+ static partial void AssertMustUseRewriter(ContractFailureKind kind, String contractKind)
+ {
+ if (_assertingMustUseRewriter)
+ System.Diagnostics.Assert.Fail("Asserting that we must use the rewriter went reentrant.", "Didn't rewrite this mscorlib?");
+ _assertingMustUseRewriter = true;
+
+ // For better diagnostics, report which assembly is at fault. Walk up stack and
+ // find the first non-mscorlib assembly.
+ Assembly thisAssembly = typeof(Contract).Assembly; // In case we refactor mscorlib, use Contract class instead of Object.
+ StackTrace stack = new StackTrace();
+ Assembly probablyNotRewritten = null;
+ for (int i = 0; i < stack.FrameCount; i++)
+ {
+ Assembly caller = stack.GetFrame(i).GetMethod().DeclaringType.Assembly;
+ if (caller != thisAssembly)
+ {
+ probablyNotRewritten = caller;
+ break;
+ }
+ }
+
+ if (probablyNotRewritten == null)
+ probablyNotRewritten = thisAssembly;
+ String simpleName = probablyNotRewritten.GetName().Name;
+ System.Runtime.CompilerServices.ContractHelper.TriggerFailure(kind, Environment.GetResourceString("MustUseCCRewrite", contractKind, simpleName), null, null, null);
+
+ _assertingMustUseRewriter = false;
+ }
+
+ #endregion Private Methods
+
+ #region Failure Behavior
+
+ /// <summary>
+ /// Without contract rewriting, failing Assert/Assumes end up calling this method.
+ /// Code going through the contract rewriter never calls this method. Instead, the rewriter produced failures call
+ /// System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent, followed by
+ /// System.Runtime.CompilerServices.ContractHelper.TriggerFailure.
+ /// </summary>
+ [SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Security.SecuritySafeCriticalAttribute")]
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ static partial void ReportFailure(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException)
+ {
+ if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume)
+ throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind");
+ Contract.EndContractBlock();
+
+ // displayMessage == null means: yes we handled it. Otherwise it is the localized failure message
+ var displayMessage = System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent(failureKind, userMessage, conditionText, innerException);
+
+ if (displayMessage == null) return;
+
+ System.Runtime.CompilerServices.ContractHelper.TriggerFailure(failureKind, displayMessage, userMessage, conditionText, innerException);
+ }
+
+ /// <summary>
+ /// Allows a managed application environment such as an interactive interpreter (IronPython)
+ /// to be notified of contract failures and
+ /// potentially "handle" them, either by throwing a particular exception type, etc. If any of the
+ /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will
+ /// not pop up an assert dialog box or trigger escalation policy. Hooking this event requires
+ /// full trust, because it will inform you of bugs in the appdomain and because the event handler
+ /// could allow you to continue execution.
+ /// </summary>
+ public static event EventHandler<ContractFailedEventArgs> ContractFailed {
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#if FEATURE_LINK_DEMAND
+ [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
+#endif
+#endif
+ add {
+ System.Runtime.CompilerServices.ContractHelper.InternalContractFailed += value;
+ }
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#if FEATURE_LINK_DEMAND
+ [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
+#endif
+#endif
+ remove {
+ System.Runtime.CompilerServices.ContractHelper.InternalContractFailed -= value;
+ }
+ }
+ #endregion FailureBehavior
+ }
+
+ public sealed class ContractFailedEventArgs : EventArgs
+ {
+ private ContractFailureKind _failureKind;
+ private String _message;
+ private String _condition;
+ private Exception _originalException;
+ private bool _handled;
+ private bool _unwind;
+
+ internal Exception thrownDuringHandler;
+
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ public ContractFailedEventArgs(ContractFailureKind failureKind, String message, String condition, Exception originalException)
+ {
+ Contract.Requires(originalException == null || failureKind == ContractFailureKind.PostconditionOnException);
+ _failureKind = failureKind;
+ _message = message;
+ _condition = condition;
+ _originalException = originalException;
+ }
+
+ public String Message { get { return _message; } }
+ public String Condition { get { return _condition; } }
+ public ContractFailureKind FailureKind { get { return _failureKind; } }
+ public Exception OriginalException { get { return _originalException; } }
+
+ // Whether the event handler "handles" this contract failure, or to fail via escalation policy.
+ public bool Handled {
+ get { return _handled; }
+ }
+
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#if FEATURE_LINK_DEMAND
+ [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
+#endif
+#endif
+ public void SetHandled()
+ {
+ _handled = true;
+ }
+
+ public bool Unwind {
+ get { return _unwind; }
+ }
+
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#if FEATURE_LINK_DEMAND
+ [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
+#endif
+#endif
+ public void SetUnwind()
+ {
+ _unwind = true;
+ }
+ }
+
+ [Serializable]
+ [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
+ internal sealed class ContractException : Exception
+ {
+ readonly ContractFailureKind _Kind;
+ readonly string _UserMessage;
+ readonly string _Condition;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public ContractFailureKind Kind { get { return _Kind; } }
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string Failure { get { return this.Message; } }
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string UserMessage { get { return _UserMessage; } }
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string Condition { get { return _Condition; } }
+
+ // Called by COM Interop, if we see COR_E_CODECONTRACTFAILED as an HRESULT.
+ private ContractException()
+ {
+ HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED;
+ }
+
+ public ContractException(ContractFailureKind kind, string failure, string userMessage, string condition, Exception innerException)
+ : base(failure, innerException)
+ {
+ HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED;
+ this._Kind = kind;
+ this._UserMessage = userMessage;
+ this._Condition = condition;
+ }
+
+ private ContractException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+ : base(info, context)
+ {
+ _Kind = (ContractFailureKind)info.GetInt32("Kind");
+ _UserMessage = info.GetString("UserMessage");
+ _Condition = info.GetString("Condition");
+ }
+
+#if FEATURE_UNTRUSTED_CALLERS && FEATURE_SERIALIZATION
+ [SecurityCritical]
+#if FEATURE_LINK_DEMAND && FEATURE_SERIALIZATION
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
+#endif // FEATURE_LINK_DEMAND
+#endif // FEATURE_UNTRUSTED_CALLERS
+ public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+
+ info.AddValue("Kind", _Kind);
+ info.AddValue("UserMessage", _UserMessage);
+ info.AddValue("Condition", _Condition);
+ }
+ }
+}
+
+
+namespace System.Runtime.CompilerServices
+{
+ public static partial class ContractHelper
+ {
+ #region Private fields
+
+ private static volatile EventHandler<ContractFailedEventArgs> contractFailedEvent;
+ private static readonly Object lockObject = new Object();
+
+ internal const int COR_E_CODECONTRACTFAILED = unchecked((int)0x80131542);
+
+ #endregion
+
+ /// <summary>
+ /// Allows a managed application environment such as an interactive interpreter (IronPython) or a
+ /// web browser host (Jolt hosting Silverlight in IE) to be notified of contract failures and
+ /// potentially "handle" them, either by throwing a particular exception type, etc. If any of the
+ /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will
+ /// not pop up an assert dialog box or trigger escalation policy. Hooking this event requires
+ /// full trust.
+ /// </summary>
+ internal static event EventHandler<ContractFailedEventArgs> InternalContractFailed
+ {
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#endif
+ add {
+ // Eagerly prepare each event handler _marked with a reliability contract_, to
+ // attempt to reduce out of memory exceptions while reporting contract violations.
+ // This only works if the new handler obeys the constraints placed on
+ // constrained execution regions. Eagerly preparing non-reliable event handlers
+ // would be a perf hit and wouldn't significantly improve reliability.
+ // UE: Please mention reliable event handlers should also be marked with the
+ // PrePrepareMethodAttribute to avoid CER eager preparation work when ngen'ed.
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareContractedDelegate(value);
+ lock (lockObject)
+ {
+ contractFailedEvent += value;
+ }
+ }
+#if FEATURE_UNTRUSTED_CALLERS
+ [SecurityCritical]
+#endif
+ remove {
+ lock (lockObject)
+ {
+ contractFailedEvent -= value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Rewriter will call this method on a contract failure to allow listeners to be notified.
+ /// The method should not perform any failure (assert/throw) itself.
+ /// This method has 3 functions:
+ /// 1. Call any contract hooks (such as listeners to Contract failed events)
+ /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null)
+ /// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently.
+ /// </summary>
+ /// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough.
+ /// On exit: null if the event was handled and should not trigger a failure.
+ /// Otherwise, returns the localized failure message</param>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_RELIABILITY_CONTRACTS
+ [SecuritySafeCritical]
+#endif
+ static partial void RaiseContractFailedEventImplementation(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException, ref string resultFailureMessage)
+ {
+ if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume)
+ throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind");
+ Contract.EndContractBlock();
+
+ string returnValue;
+ String displayMessage = "contract failed."; // Incomplete, but in case of OOM during resource lookup...
+ ContractFailedEventArgs eventArgs = null; // In case of OOM.
+#if FEATURE_RELIABILITY_CONTRACTS
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+#endif
+ try
+ {
+ displayMessage = GetDisplayMessage(failureKind, userMessage, conditionText);
+ EventHandler<ContractFailedEventArgs> contractFailedEventLocal = contractFailedEvent;
+ if (contractFailedEventLocal != null)
+ {
+ eventArgs = new ContractFailedEventArgs(failureKind, displayMessage, conditionText, innerException);
+ foreach (EventHandler<ContractFailedEventArgs> handler in contractFailedEventLocal.GetInvocationList())
+ {
+ try
+ {
+ handler(null, eventArgs);
+ }
+ catch (Exception e)
+ {
+ eventArgs.thrownDuringHandler = e;
+ eventArgs.SetUnwind();
+ }
+ }
+ if (eventArgs.Unwind)
+ {
+#if !FEATURE_CORECLR
+ if (Environment.IsCLRHosted)
+ TriggerCodeContractEscalationPolicy(failureKind, displayMessage, conditionText, innerException);
+#endif
+ // unwind
+ if (innerException == null) { innerException = eventArgs.thrownDuringHandler; }
+ throw new ContractException(failureKind, displayMessage, userMessage, conditionText, innerException);
+ }
+ }
+ }
+ finally
+ {
+ if (eventArgs != null && eventArgs.Handled)
+ {
+ returnValue = null; // handled
+ }
+ else
+ {
+ returnValue = displayMessage;
+ }
+ }
+ resultFailureMessage = returnValue;
+ }
+
+ /// <summary>
+ /// Rewriter calls this method to get the default failure behavior.
+ /// </summary>
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "conditionText")]
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "userMessage")]
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")]
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "innerException")]
+ [System.Diagnostics.DebuggerNonUserCode]
+#if FEATURE_UNTRUSTED_CALLERS && !FEATURE_CORECLR
+ [SecuritySafeCritical]
+#endif
+ static partial void TriggerFailureImplementation(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException)
+ {
+ // If we're here, our intent is to pop up a dialog box (if we can). For developers
+ // interacting live with a debugger, this is a good experience. For Silverlight
+ // hosted in Internet Explorer, the assert window is great. If we cannot
+ // pop up a dialog box, throw an exception (consider a library compiled with
+ // "Assert On Failure" but used in a process that can't pop up asserts, like an
+ // NT Service). For the CLR hosted by server apps like SQL or Exchange, we should
+ // trigger escalation policy.
+#if !FEATURE_CORECLR
+ if (Environment.IsCLRHosted)
+ {
+ TriggerCodeContractEscalationPolicy(kind, displayMessage, conditionText, innerException);
+ // Hosts like SQL may choose to abort the thread, so we will not get here in all cases.
+ // But if the host's chosen action was to throw an exception, we should throw an exception
+ // here (which is easier to do in managed code with the right parameters).
+ throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException);
+ }
+#endif // !FEATURE_CORECLR
+ if (!Environment.UserInteractive) {
+ throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException);
+ }
+ // May need to rethink Assert.Fail w/ TaskDialogIndirect as a model. Window title. Main instruction. Content. Expanded info.
+ // Optional info like string for collapsed text vs. expanded text.
+ String windowTitle = Environment.GetResourceString(GetResourceNameForFailure(kind));
+ const int numStackFramesToSkip = 2; // To make stack traces easier to read
+ System.Diagnostics.Assert.Fail(conditionText, displayMessage, windowTitle, COR_E_CODECONTRACTFAILED, StackTrace.TraceFormat.Normal, numStackFramesToSkip);
+ // If we got here, the user selected Ignore. Continue.
+ }
+
+ private static String GetResourceNameForFailure(ContractFailureKind failureKind)
+ {
+ String resourceName = null;
+ switch (failureKind)
+ {
+ case ContractFailureKind.Assert:
+ resourceName = "AssertionFailed";
+ break;
+
+ case ContractFailureKind.Assume:
+ resourceName = "AssumptionFailed";
+ break;
+
+ case ContractFailureKind.Precondition:
+ resourceName = "PreconditionFailed";
+ break;
+
+ case ContractFailureKind.Postcondition:
+ resourceName = "PostconditionFailed";
+ break;
+
+ case ContractFailureKind.Invariant:
+ resourceName = "InvariantFailed";
+ break;
+
+ case ContractFailureKind.PostconditionOnException:
+ resourceName = "PostconditionOnExceptionFailed";
+ break;
+
+ default:
+ Contract.Assume(false, "Unreachable code");
+ resourceName = "AssumptionFailed";
+ break;
+ }
+ return resourceName;
+ }
+
+#if FEATURE_RELIABILITY_CONTRACTS
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ private static String GetDisplayMessage(ContractFailureKind failureKind, String userMessage, String conditionText)
+ {
+ String resourceName = GetResourceNameForFailure(failureKind);
+ // Well-formatted English messages will take one of four forms. A sentence ending in
+ // either a period or a colon, the condition string, then the message tacked
+ // on to the end with two spaces in front.
+ // Note that both the conditionText and userMessage may be null. Also,
+ // on Silverlight we may not be able to look up a friendly string for the
+ // error message. Let's leverage Silverlight's default error message there.
+ String failureMessage;
+ if (!String.IsNullOrEmpty(conditionText)) {
+ resourceName += "_Cnd";
+ failureMessage = Environment.GetResourceString(resourceName, conditionText);
+ }
+ else {
+ failureMessage = Environment.GetResourceString(resourceName);
+ }
+
+ // Now add in the user message, if present.
+ if (!String.IsNullOrEmpty(userMessage))
+ {
+ return failureMessage + " " + userMessage;
+ }
+ else
+ {
+ return failureMessage;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ // Will trigger escalation policy, if hosted and the host requested us to do something (such as
+ // abort the thread or exit the process). Starting in Dev11, for hosted apps the default behavior
+ // is to throw an exception.
+ // Implementation notes:
+ // We implement our default behavior of throwing an exception by simply returning from our native
+ // method inside the runtime and falling through to throw an exception.
+ // We must call through this method before calling the method on the Environment class
+ // because our security team does not yet support SecuritySafeCritical on P/Invoke methods.
+ // Note this can be called in the context of throwing another exception (EnsuresOnThrow).
+ [SecuritySafeCritical]
+ [DebuggerNonUserCode]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private static void TriggerCodeContractEscalationPolicy(ContractFailureKind failureKind, String message, String conditionText, Exception innerException)
+ {
+ String exceptionAsString = null;
+ if (innerException != null)
+ exceptionAsString = innerException.ToString();
+ Environment.TriggerCodeContractFailure(failureKind, message, conditionText, exceptionAsString);
+ }
+#endif // !FEATURE_CORECLR
+ }
+} // namespace System.Runtime.CompilerServices
+
diff --git a/src/mscorlib/src/System/Diagnostics/Debugger.cs b/src/mscorlib/src/System/Diagnostics/Debugger.cs
new file mode 100644
index 0000000000..339c89eecf
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Debugger.cs
@@ -0,0 +1,195 @@
+// 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.
+
+// The Debugger class is a part of the System.Diagnostics package
+// and is used for communicating with a debugger.
+
+namespace System.Diagnostics
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Reflection;
+ using System.Runtime.CompilerServices;
+ using System.Security;
+ using System.Security.Permissions;
+ using System.Runtime.Versioning;
+
+ // No data, does not need to be marked with the serializable attribute
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class Debugger
+ {
+ // This should have been a static class, but wasn't as of v3.5. Clearly, this is
+ // broken. We'll keep this in V4 for binary compat, but marked obsolete as error
+ // so migrated source code gets fixed.
+ [Obsolete("Do not create instances of the Debugger class. Call the static methods directly on this type instead", true)]
+ public Debugger()
+ {
+ // Should not have been instantiable - here for binary compatibility in V4.
+ }
+
+ // Break causes a breakpoint to be signalled to an attached debugger. If no debugger
+ // is attached, the user is asked if he wants to attach a debugger. If yes, then the
+ // debugger is launched.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void Break()
+ {
+ if (!Debugger.IsAttached)
+ {
+ // Try and demand UnmanagedCodePermission. This is done in a try block because if this
+ // fails we want to be able to silently eat the exception and just return so
+ // that the call to Break does not possibly cause an unhandled exception.
+ // The idea here is that partially trusted code shouldn't be able to launch a debugger
+ // without the user going through Watson.
+ try
+ {
+#pragma warning disable 618
+ new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
+#pragma warning restore 618
+ }
+
+ // If we enter this block, we do not have permission to break into the debugger
+ // and so we just return.
+ catch (SecurityException)
+ {
+ return;
+ }
+ }
+
+ // Causing a break is now allowed.
+ BreakInternal();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ static void BreakCanThrow()
+ {
+ if (!Debugger.IsAttached)
+ {
+#pragma warning disable 618
+ new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
+#pragma warning restore 618
+ }
+
+ // Causing a break is now allowed.
+ BreakInternal();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void BreakInternal();
+
+ // Launch launches & attaches a debugger to the process. If a debugger is already attached,
+ // nothing happens.
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static bool Launch()
+ {
+ if (Debugger.IsAttached)
+ return (true);
+
+ // Try and demand UnmanagedCodePermission. This is done in a try block because if this
+ // fails we want to be able to silently eat the exception and just return so
+ // that the call to Break does not possibly cause an unhandled exception.
+ // The idea here is that partially trusted code shouldn't be able to launch a debugger
+ // without the user going through Watson.
+ try
+ {
+#pragma warning disable 618
+ new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
+#pragma warning restore 618
+ }
+
+ // If we enter this block, we do not have permission to break into the debugger
+ // and so we just return.
+ catch (SecurityException)
+ {
+ return (false);
+ }
+
+ // Causing the debugger to launch is now allowed.
+ return (LaunchInternal());
+ }
+
+ // This class implements code:ICustomDebuggerNotification and provides a type to be used to notify
+ // the debugger that execution is about to enter a path that involves a cross-thread dependency.
+ // See code:NotifyOfCrossThreadDependency for more details.
+ private class CrossThreadDependencyNotification : ICustomDebuggerNotification
+ {
+ // constructor
+ public CrossThreadDependencyNotification()
+ {
+ }
+ }
+
+ // Do not inline the slow path
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ private static void NotifyOfCrossThreadDependencySlow()
+ {
+ CrossThreadDependencyNotification notification = new CrossThreadDependencyNotification();
+ CustomNotification(notification);
+ }
+
+ // Sends a notification to the debugger to indicate that execution is about to enter a path
+ // involving a cross thread dependency. A debugger that has opted into this type of notification
+ // can take appropriate action on receipt. For example, performing a funceval normally requires
+ // freezing all threads but the one performing the funceval. If the funceval requires execution on
+ // more than one thread, as might occur in remoting scenarios, the funceval will block. This
+ // notification will apprise the debugger that it will need to slip a thread or abort the funceval
+ // in such a situation. The notification is subject to collection after this function returns.
+ //
+ [method:System.Runtime.InteropServices.ComVisible(false)]
+ public static void NotifyOfCrossThreadDependency()
+ {
+ if (Debugger.IsAttached)
+ {
+ NotifyOfCrossThreadDependencySlow();
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool LaunchInternal();
+
+ // Returns whether or not a debugger is attached to the process.
+ //
+ public static extern bool IsAttached
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ get;
+ }
+
+ // Constants representing the importance level of messages to be logged.
+ //
+ // An attached debugger can enable or disable which messages will
+ // actually be reported to the user through the COM+ debugger
+ // services API. This info is communicated to the runtime so only
+ // desired events are actually reported to the debugger.
+ //
+ // Constant representing the default category
+ public static readonly String DefaultCategory = null;
+
+ // Posts a message for the attached debugger. If there is no
+ // debugger attached, has no effect. The debugger may or may not
+ // report the message depending on its settings.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern void Log(int level, String category, String message);
+
+ // Checks to see if an attached debugger has logging enabled
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern bool IsLogging();
+
+ // Posts a custom notification for the attached debugger. If there is no
+ // debugger attached, has no effect. The debugger may or may not
+ // report the notification depending on its settings.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void CustomNotification(ICustomDebuggerNotification data);
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Diagnostics/DebuggerAttributes.cs b/src/mscorlib/src/System/Diagnostics/DebuggerAttributes.cs
new file mode 100644
index 0000000000..6adcf34eda
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/DebuggerAttributes.cs
@@ -0,0 +1,378 @@
+// 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.
+
+/*============================================================
+**
+**
+**
+** Purpose: Attributes for debugger
+**
+**
+===========================================================*/
+
+
+namespace System.Diagnostics {
+ using System;
+ using System.Runtime.InteropServices;
+ using System.Diagnostics.Contracts;
+
+[Serializable]
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
+ [ComVisible(true)]
+ public sealed class DebuggerStepThroughAttribute : Attribute
+ {
+ public DebuggerStepThroughAttribute () {}
+ }
+
+[Serializable]
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
+ [ComVisible(true)]
+ public sealed class DebuggerStepperBoundaryAttribute : Attribute
+ {
+ public DebuggerStepperBoundaryAttribute () {}
+ }
+
+[Serializable]
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)]
+ [ComVisible(true)]
+ public sealed class DebuggerHiddenAttribute : Attribute
+ {
+ public DebuggerHiddenAttribute () {}
+ }
+
+[Serializable]
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor |AttributeTargets.Struct, Inherited = false)]
+ [ComVisible(true)]
+ public sealed class DebuggerNonUserCodeAttribute : Attribute
+ {
+ public DebuggerNonUserCodeAttribute () {}
+ }
+
+ // Attribute class used by the compiler to mark modules.
+ // If present, then debugging information for everything in the
+ // assembly was generated by the compiler, and will be preserved
+ // by the Runtime so that the debugger can provide full functionality
+ // in the case of JIT attach. If not present, then the compiler may
+ // or may not have included debugging information, and the Runtime
+ // won't preserve the debugging info, which will make debugging after
+ // a JIT attach difficult.
+ [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Module, AllowMultiple = false)]
+ [ComVisible(true)]
+ public sealed class DebuggableAttribute : Attribute
+ {
+ [Flags]
+ [ComVisible(true)]
+ public enum DebuggingModes
+ {
+ None = 0x0,
+ Default = 0x1,
+ DisableOptimizations = 0x100,
+ IgnoreSymbolStoreSequencePoints = 0x2,
+ EnableEditAndContinue = 0x4
+ }
+
+ private DebuggingModes m_debuggingModes;
+
+ public DebuggableAttribute(bool isJITTrackingEnabled,
+ bool isJITOptimizerDisabled)
+ {
+ m_debuggingModes = 0;
+
+ if (isJITTrackingEnabled)
+ {
+ m_debuggingModes |= DebuggingModes.Default;
+ }
+
+ if (isJITOptimizerDisabled)
+ {
+ m_debuggingModes |= DebuggingModes.DisableOptimizations;
+ }
+ }
+
+ public DebuggableAttribute(DebuggingModes modes)
+ {
+ m_debuggingModes = modes;
+ }
+
+ public bool IsJITTrackingEnabled
+ {
+ get { return ((m_debuggingModes & DebuggingModes.Default) != 0); }
+ }
+
+ public bool IsJITOptimizerDisabled
+ {
+ get { return ((m_debuggingModes & DebuggingModes.DisableOptimizations) != 0); }
+ }
+
+ public DebuggingModes DebuggingFlags
+ {
+ get { return m_debuggingModes; }
+ }
+ }
+
+ // DebuggerBrowsableState states are defined as follows:
+ // Never never show this element
+ // Expanded expansion of the class is done, so that all visible internal members are shown
+ // Collapsed expansion of the class is not performed. Internal visible members are hidden
+ // RootHidden The target element itself should not be shown, but should instead be
+ // automatically expanded to have its members displayed.
+ // Default value is collapsed
+
+ // Please also change the code which validates DebuggerBrowsableState variable (in this file)
+ // if you change this enum.
+ [ComVisible(true)]
+ public enum DebuggerBrowsableState
+ {
+ Never = 0,
+ //Expanded is not supported in this release
+ //Expanded = 1,
+ Collapsed = 2,
+ RootHidden = 3
+ }
+
+
+ // the one currently supported with the csee.dat
+ // (mcee.dat, autoexp.dat) file.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ [ComVisible(true)]
+ public sealed class DebuggerBrowsableAttribute: Attribute
+ {
+ private DebuggerBrowsableState state;
+ public DebuggerBrowsableAttribute(DebuggerBrowsableState state)
+ {
+ if( state < DebuggerBrowsableState.Never || state > DebuggerBrowsableState.RootHidden)
+ throw new ArgumentOutOfRangeException("state");
+ Contract.EndContractBlock();
+
+ this.state = state;
+ }
+ public DebuggerBrowsableState State
+ {
+ get { return state; }
+ }
+ }
+
+
+ // DebuggerTypeProxyAttribute
+ [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
+ [ComVisible(true)]
+ public sealed class DebuggerTypeProxyAttribute: Attribute
+ {
+ private string typeName;
+ private string targetName;
+ private Type target;
+
+ public DebuggerTypeProxyAttribute(Type type)
+ {
+ if (type == null) {
+ throw new ArgumentNullException("type");
+ }
+ Contract.EndContractBlock();
+
+ this.typeName = type.AssemblyQualifiedName;
+ }
+
+ public DebuggerTypeProxyAttribute(string typeName)
+ {
+ this.typeName = typeName;
+ }
+ public string ProxyTypeName
+ {
+ get { return typeName; }
+ }
+
+ public Type Target
+ {
+ set {
+ if( value == null) {
+ throw new ArgumentNullException("value");
+ }
+ Contract.EndContractBlock();
+
+ targetName = value.AssemblyQualifiedName;
+ target = value;
+ }
+
+ get { return target; }
+ }
+
+ public string TargetTypeName
+ {
+ get { return targetName; }
+ set { targetName = value; }
+
+ }
+ }
+
+ // This attribute is used to control what is displayed for the given class or field
+ // in the data windows in the debugger. The single argument to this attribute is
+ // the string that will be displayed in the value column for instances of the type.
+ // This string can include text between { and } which can be either a field,
+ // property or method (as will be documented in mscorlib). In the C# case,
+ // a general expression will be allowed which only has implicit access to the this pointer
+ // for the current instance of the target type. The expression will be limited,
+ // however: there is no access to aliases, locals, or pointers.
+ // In addition, attributes on properties referenced in the expression are not processed.
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Assembly, AllowMultiple = true)]
+ [ComVisible(true)]
+ public sealed class DebuggerDisplayAttribute : Attribute
+ {
+ private string name;
+ private string value;
+ private string type;
+ private string targetName;
+ private Type target;
+
+ public DebuggerDisplayAttribute(string value)
+ {
+ if( value == null ) {
+ this.value = "";
+ }
+ else {
+ this.value = value;
+ }
+ name = "";
+ type = "";
+ }
+
+ public string Value
+ {
+ get { return this.value; }
+ }
+
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+
+ public string Type
+ {
+ get { return type; }
+ set { type = value; }
+ }
+
+ public Type Target
+ {
+ set {
+ if( value == null) {
+ throw new ArgumentNullException("value");
+ }
+ Contract.EndContractBlock();
+
+ targetName = value.AssemblyQualifiedName;
+ target = value;
+ }
+ get { return target; }
+ }
+
+ public string TargetTypeName
+ {
+ get { return targetName; }
+ set { targetName = value; }
+
+ }
+ }
+
+
+ /// <summary>
+ /// Signifies that the attributed type has a visualizer which is pointed
+ /// to by the parameter type name strings.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
+ [ComVisible(true)]
+ public sealed class DebuggerVisualizerAttribute: Attribute
+ {
+ private string visualizerObjectSourceName;
+ private string visualizerName;
+ private string description;
+ private string targetName;
+ private Type target;
+
+ public DebuggerVisualizerAttribute(string visualizerTypeName)
+ {
+ this.visualizerName = visualizerTypeName;
+ }
+ public DebuggerVisualizerAttribute(string visualizerTypeName, string visualizerObjectSourceTypeName)
+ {
+ this.visualizerName = visualizerTypeName;
+ this.visualizerObjectSourceName = visualizerObjectSourceTypeName;
+ }
+ public DebuggerVisualizerAttribute(string visualizerTypeName, Type visualizerObjectSource)
+ {
+ if (visualizerObjectSource == null) {
+ throw new ArgumentNullException("visualizerObjectSource");
+ }
+ Contract.EndContractBlock();
+ this.visualizerName = visualizerTypeName;
+ this.visualizerObjectSourceName = visualizerObjectSource.AssemblyQualifiedName;
+ }
+ public DebuggerVisualizerAttribute(Type visualizer)
+ {
+ if (visualizer == null) {
+ throw new ArgumentNullException("visualizer");
+ }
+ Contract.EndContractBlock();
+ this.visualizerName = visualizer.AssemblyQualifiedName;
+ }
+ public DebuggerVisualizerAttribute(Type visualizer, Type visualizerObjectSource)
+ {
+ if (visualizer == null) {
+ throw new ArgumentNullException("visualizer");
+ }
+ if (visualizerObjectSource == null) {
+ throw new ArgumentNullException("visualizerObjectSource");
+ }
+ Contract.EndContractBlock();
+ this.visualizerName = visualizer.AssemblyQualifiedName;
+ this.visualizerObjectSourceName = visualizerObjectSource.AssemblyQualifiedName;
+ }
+ public DebuggerVisualizerAttribute(Type visualizer, string visualizerObjectSourceTypeName)
+ {
+ if (visualizer == null) {
+ throw new ArgumentNullException("visualizer");
+ }
+ Contract.EndContractBlock();
+ this.visualizerName = visualizer.AssemblyQualifiedName;
+ this.visualizerObjectSourceName = visualizerObjectSourceTypeName;
+ }
+
+ public string VisualizerObjectSourceTypeName
+ {
+ get { return visualizerObjectSourceName; }
+ }
+ public string VisualizerTypeName
+ {
+ get { return visualizerName; }
+ }
+ public string Description
+ {
+ get { return description; }
+ set { description = value; }
+ }
+
+ public Type Target
+ {
+ set {
+ if( value == null) {
+ throw new ArgumentNullException("value");
+ }
+ Contract.EndContractBlock();
+
+ targetName = value.AssemblyQualifiedName;
+ target = value;
+ }
+
+ get { return target; }
+ }
+
+ public string TargetTypeName
+ {
+ set { targetName = value; }
+ get { return targetName; }
+ }
+ }
+}
+
+
diff --git a/src/mscorlib/src/System/Diagnostics/EditAndContinueHelper.cs b/src/mscorlib/src/System/Diagnostics/EditAndContinueHelper.cs
new file mode 100644
index 0000000000..32d7a98a50
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/EditAndContinueHelper.cs
@@ -0,0 +1,27 @@
+// 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.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Helper for EditAndContinue
+**
+**
+=============================================================================*/
+
+namespace System.Diagnostics {
+
+ using System;
+
+ [Serializable]
+ internal sealed class EditAndContinueHelper
+ {
+#pragma warning disable 169
+#pragma warning disable 414 // Field is not used from managed.
+ private Object _objectReference;
+#pragma warning restore 414
+#pragma warning restore 169
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/ActivityTracker.cs b/src/mscorlib/src/System/Diagnostics/Eventing/ActivityTracker.cs
new file mode 100644
index 0000000000..a7124a26ff
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/ActivityTracker.cs
@@ -0,0 +1,670 @@
+// 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.Diagnostics;
+using System.Threading;
+#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
+using System.Threading.Tasks;
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Tracks activities. This is meant to be a singleton (accessed by the ActivityTracer.Instance static property)
+ ///
+ /// Logically this is simply holds the m_current variable that holds the async local that holds the current ActivityInfo
+ /// An ActivityInfo is represents a activity (which knows its creator and thus knows its path).
+ ///
+ /// Most of the magic is in the async local (it gets copied to new tasks)
+ ///
+ /// On every start event call OnStart
+ ///
+ /// Guid activityID;
+ /// Guid relatedActivityID;
+ /// if (OnStart(activityName, out activityID, out relatedActivityID, ForceStop, options))
+ /// // Log Start event with activityID and relatedActivityID
+ ///
+ /// On every stop event call OnStop
+ ///
+ /// Guid activityID;
+ /// if (OnStop(activityName, ref activityID ForceStop))
+ /// // Stop event with activityID
+ ///
+ /// On any normal event log the event with activityTracker.CurrentActivityId
+ /// </summary>
+ internal class ActivityTracker
+ {
+
+ /// <summary>
+ /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix.
+ /// It updates CurrentActivityId to track.
+ ///
+ /// It returns true if the Start should be logged, otherwise (if it is illegal recursion) it return false.
+ ///
+ /// The start event should use as its activity ID the CurrentActivityId AFTER calling this routine and its
+ /// RelatedActivityID the CurrentActivityId BEFORE calling this routine (the creator).
+ ///
+ /// If activity tracing is not on, then activityId and relatedActivityId are not set
+ /// </summary>
+ public void OnStart(string providerName, string activityName, int task, ref Guid activityId, ref Guid relatedActivityId, EventActivityOptions options)
+ {
+ if (m_current == null) // We are not enabled
+ {
+ // We used to rely on the TPL provider turning us on, but that has the disadvantage that you don't get Start-Stop tracking
+ // until you use Tasks for the first time (which you may never do). Thus we change it to pull rather tan push for whether
+ // we are enabled.
+ if (m_checkedForEnable)
+ return;
+ m_checkedForEnable = true;
+ if (TplEtwProvider.Log.IsEnabled(EventLevel.Informational, TplEtwProvider.Keywords.TasksFlowActivityIds))
+ Enable();
+ if (m_current == null)
+ return;
+ }
+
+
+ Contract.Assert((options & EventActivityOptions.Disable) == 0);
+
+ var currentActivity = m_current.Value;
+ var fullActivityName = NormalizeActivityName(providerName, activityName, task);
+
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStartEnter", fullActivityName);
+ etwLog.DebugFacilityMessage("OnStartEnterActivityState", ActivityInfo.LiveActivities(currentActivity));
+ }
+
+ if (currentActivity != null)
+ {
+ // Stop activity tracking if we reached the maximum allowed depth
+ if (currentActivity.m_level >= MAX_ACTIVITY_DEPTH)
+ {
+ activityId = Guid.Empty;
+ relatedActivityId = Guid.Empty;
+ if (etwLog.Debug)
+ etwLog.DebugFacilityMessage("OnStartRET", "Fail");
+ return;
+ }
+ // Check for recursion, and force-stop any activities if the activity already started.
+ if ((options & EventActivityOptions.Recursive) == 0)
+ {
+ ActivityInfo existingActivity = FindActiveActivity(fullActivityName, currentActivity);
+ if (existingActivity != null)
+ {
+ OnStop(providerName, activityName, task, ref activityId);
+ currentActivity = m_current.Value;
+ }
+ }
+ }
+
+ // Get a unique ID for this activity.
+ long id;
+ if (currentActivity == null)
+ id = Interlocked.Increment(ref m_nextId);
+ else
+ id = Interlocked.Increment(ref currentActivity.m_lastChildID);
+
+ // The previous ID is my 'causer' and becomes my related activity ID
+ relatedActivityId = EventSource.CurrentThreadActivityId;
+
+ // Add to the list of started but not stopped activities.
+ ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, relatedActivityId, options);
+ m_current.Value = newActivity;
+
+ // Remember the current ID so we can log it
+ activityId = newActivity.ActivityId;
+
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStartRetActivityState", ActivityInfo.LiveActivities(newActivity));
+ etwLog.DebugFacilityMessage1("OnStartRet", activityId.ToString(), relatedActivityId.ToString());
+ }
+ }
+
+ /// <summary>
+ /// Called when a work item stops. The activity name = providerName + activityName without 'Stop' suffix.
+ /// It updates m_current variable to track this fact. The Stop event associated with stop should log the ActivityID associated with the event.
+ ///
+ /// If activity tracing is not on, then activityId and relatedActivityId are not set
+ /// </summary>
+ public void OnStop(string providerName, string activityName, int task, ref Guid activityId)
+ {
+ if (m_current == null) // We are not enabled
+ return;
+
+ var fullActivityName = NormalizeActivityName(providerName, activityName, task);
+
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStopEnter", fullActivityName);
+ etwLog.DebugFacilityMessage("OnStopEnterActivityState", ActivityInfo.LiveActivities(m_current.Value));
+ }
+
+ for (; ; ) // This is a retry loop.
+ {
+ ActivityInfo currentActivity = m_current.Value;
+ ActivityInfo newCurrentActivity = null; // if we have seen any live activities (orphans), at he first one we have seen.
+
+ // Search to find the activity to stop in one pass. This insures that we don't let one mistake
+ // (stopping something that was not started) cause all active starts to be stopped
+ // By first finding the target start to stop we are more robust.
+ ActivityInfo activityToStop = FindActiveActivity(fullActivityName, currentActivity);
+
+ // ignore stops where we can't find a start because we may have popped them previously.
+ if (activityToStop == null)
+ {
+ activityId = Guid.Empty;
+ // TODO add some logging about this. Basically could not find matching start.
+ if (etwLog.Debug)
+ etwLog.DebugFacilityMessage("OnStopRET", "Fail");
+ return;
+ }
+
+ activityId = activityToStop.ActivityId;
+
+ // See if there are any orphans that need to be stopped.
+ ActivityInfo orphan = currentActivity;
+ while (orphan != activityToStop && orphan != null)
+ {
+ if (orphan.m_stopped != 0) // Skip dead activities.
+ {
+ orphan = orphan.m_creator;
+ continue;
+ }
+ if (orphan.CanBeOrphan())
+ {
+ // We can't pop anything after we see a valid orphan, remember this for later when we update m_current.
+ if (newCurrentActivity == null)
+ newCurrentActivity = orphan;
+ }
+ else
+ {
+ orphan.m_stopped = 1;
+ Contract.Assert(orphan.m_stopped != 0);
+ }
+ orphan = orphan.m_creator;
+ }
+
+ // try to Stop the activity atomically. Other threads may be trying to do this as well.
+ if (Interlocked.CompareExchange(ref activityToStop.m_stopped, 1, 0) == 0)
+ {
+ // I succeeded stopping this activity. Now we update our m_current pointer
+
+ // If I haven't yet determined the new current activity, it is my creator.
+ if (newCurrentActivity == null)
+ newCurrentActivity = activityToStop.m_creator;
+
+ m_current.Value = newCurrentActivity;
+
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStopRetActivityState", ActivityInfo.LiveActivities(newCurrentActivity));
+ etwLog.DebugFacilityMessage("OnStopRet", activityId.ToString());
+ }
+ return;
+ }
+ // We failed to stop it. We must have hit a race to stop it. Just start over and try again.
+ }
+ }
+
+ /// <summary>
+ /// Turns on activity tracking. It is sticky, once on it stays on (race issues otherwise)
+ /// </summary>
+ [System.Security.SecuritySafeCritical]
+ public void Enable()
+ {
+ if (m_current == null)
+ {
+ // Catch the not Implemented
+ try
+ {
+ m_current = new AsyncLocal<ActivityInfo>(ActivityChanging);
+ }
+ catch (NotImplementedException) {
+#if (!ES_BUILD_PCL && ! PROJECTN)
+ // send message to debugger without delay
+ System.Diagnostics.Debugger.Log(0, null, "Activity Enabled() called but AsyncLocals Not Supported (pre V4.6). Ignoring Enable");
+#endif
+ }
+ }
+ }
+
+ /// <summary>
+ /// An activity tracker is a singleton, this is how you get the one and only instance.
+ /// </summary>
+ public static ActivityTracker Instance { get { return s_activityTrackerInstance; } }
+
+
+ #region private
+
+ /// <summary>
+ /// The current activity ID. Use this to log normal events.
+ /// </summary>
+ private Guid CurrentActivityId { get { return m_current.Value.ActivityId; } }
+
+ /// <summary>
+ /// Searched for a active (nonstopped) activity with the given name. Returns null if not found.
+ /// </summary>
+ private ActivityInfo FindActiveActivity(string name, ActivityInfo startLocation)
+ {
+ var activity = startLocation;
+ while (activity != null)
+ {
+ if (name == activity.m_name && activity.m_stopped == 0)
+ return activity;
+ activity = activity.m_creator;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Strip out "Start" or "End" suffix from activity name and add providerName prefix.
+ /// If 'task' it does not end in Start or Stop and Task is non-zero use that as the name of the activity
+ /// </summary>
+ private string NormalizeActivityName(string providerName, string activityName, int task)
+ {
+ if (activityName.EndsWith(EventSource.s_ActivityStartSuffix, StringComparison.Ordinal))
+ activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStartSuffix.Length);
+ else if (activityName.EndsWith(EventSource.s_ActivityStopSuffix, StringComparison.Ordinal))
+ activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStopSuffix.Length);
+ else if (task != 0)
+ activityName = "task" + task.ToString();
+
+ // We use provider name to distinguish between activities from different providers.
+ return providerName + activityName;
+ }
+
+ // *******************************************************************************
+ /// <summary>
+ /// An ActivityInfo represents a particular activity. It is almost read-only. The only
+ /// fields that change after creation are
+ /// m_lastChildID - used to generate unique IDs for the children activities and for the most part can be ignored.
+ /// m_stopped - indicates that this activity is dead
+ /// This read-only-ness is important because an activity's m_creator chain forms the
+ /// 'Path of creation' for the activity (which is also its unique ID) but is also used as
+ /// the 'list of live parents' which indicate of those ancestors, which are alive (if they
+ /// are not marked dead they are alive).
+ /// </summary>
+ private class ActivityInfo
+ {
+ public ActivityInfo(string name, long uniqueId, ActivityInfo creator, Guid activityIDToRestore, EventActivityOptions options)
+ {
+ m_name = name;
+ m_eventOptions = options;
+ m_creator = creator;
+ m_uniqueId = uniqueId;
+ m_level = creator != null ? creator.m_level + 1 : 0;
+ m_activityIdToRestore = activityIDToRestore;
+
+ // Create a nice GUID that encodes the chain of activities that started this one.
+ CreateActivityPathGuid(out m_guid, out m_activityPathGuidOffset);
+ }
+
+ public Guid ActivityId
+ {
+ get
+ {
+ return m_guid;
+ }
+ }
+
+ public static string Path(ActivityInfo activityInfo)
+ {
+ if (activityInfo == null)
+ return ("");
+ return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId.ToString();
+ }
+
+ public override string ToString()
+ {
+ return m_name + "(" + Path(this) + (m_stopped != 0 ? ",DEAD)" : ")");
+ }
+
+ public static string LiveActivities(ActivityInfo list)
+ {
+ if (list == null)
+ return "";
+ return list.ToString() + ";" + LiveActivities(list.m_creator);
+ }
+
+ public bool CanBeOrphan()
+ {
+ if ((m_eventOptions & EventActivityOptions.Detachable) != 0)
+ return true;
+ return false;
+ }
+
+ #region private
+
+ #region CreateActivityPathGuid
+ /// <summary>
+ /// Logically every activity Path (see Path()) that describes the activities that caused this
+ /// (rooted in an activity that predates activity tracking.
+ ///
+ /// We wish to encode this path in the Guid to the extent that we can. Many of the paths have
+ /// many small numbers in them and we take advantage of this in the encoding to output as long
+ /// a path in the GUID as possible.
+ ///
+ /// Because of the possibility of GUID collision, we only use 96 of the 128 bits of the GUID
+ /// for encoding the path. The last 32 bits are a simple checksum (and random number) that
+ /// identifies this as using the convention defined here.
+ ///
+ /// It returns both the GUID which has the path as well as the offset that points just beyond
+ /// the end of the activity (so it can be appended to). Note that if the end is in a nibble
+ /// (it uses nibbles instead of bytes as the unit of encoding, then it will point at the unfinished
+ /// byte (since the top nibble can't be zero you can determine if this is true by seeing if
+ /// this byte is nonZero. This offset is needed to efficiently create the ID for child activities.
+ /// </summary>
+ [System.Security.SecuritySafeCritical]
+ private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathGuidOffset)
+ {
+ fixed (Guid* outPtr = &idRet)
+ {
+ int activityPathGuidOffsetStart = 0;
+ if (m_creator != null)
+ {
+ activityPathGuidOffsetStart = m_creator.m_activityPathGuidOffset;
+ idRet = m_creator.m_guid;
+ }
+ else
+ {
+ // TODO FIXME - differentiate between AD inside PCL
+ int appDomainID = 0;
+#if (!ES_BUILD_STANDALONE && !PROJECTN)
+ appDomainID = System.Threading.Thread.GetDomainID();
+#endif
+ // We start with the appdomain number to make this unique among appdomains.
+ activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)appDomainID);
+ }
+
+ activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)m_uniqueId);
+
+
+ // If the path does not fit, Make a GUID by incrementing rather than as a path, keeping as much of the path as possible
+ if (12 < activityPathGuidOffset)
+ CreateOverflowGuid(outPtr);
+ }
+ }
+
+ /// <summary>
+ /// If we can't fit the activity Path into the GUID we come here. What we do is simply
+ /// generate a 4 byte number (s_nextOverflowId). Then look for an ancestor that has
+ /// sufficient space for this ID. By doing this, we preserve the fact that this activity
+ /// is a child (of unknown depth) from that ancestor.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ private unsafe void CreateOverflowGuid(Guid* outPtr)
+ {
+ // Search backwards for an ancestor that has sufficient space to put the ID.
+ for (ActivityInfo ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator)
+ {
+ if (ancestor.m_activityPathGuidOffset <= 10) // we need at least 2 bytes.
+ {
+ uint id = unchecked((uint)Interlocked.Increment(ref ancestor.m_lastChildID)); // Get a unique ID
+ // Try to put the ID into the GUID
+ *outPtr = ancestor.m_guid;
+ int endId = AddIdToGuid(outPtr, ancestor.m_activityPathGuidOffset, id, true);
+
+ // Does it fit?
+ if (endId <= 12)
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// The encoding for a list of numbers used to make Activity GUIDs. Basically
+ /// we operate on nibbles (which are nice because they show up as hex digits). The
+ /// list is ended with a end nibble (0) and depending on the nibble value (Below)
+ /// the value is either encoded into nibble itself or it can spill over into the
+ /// bytes that follow.
+ /// </summary>
+ enum NumberListCodes : byte
+ {
+ End = 0x0, // ends the list. No valid value has this prefix.
+ LastImmediateValue = 0xA,
+
+ PrefixCode = 0xB, // all the 'long' encodings go here. If the next nibble is MultiByte1-4
+ // than this is a 'overflow' id. Unlike the hierarchical IDs these are
+ // allocated densely but don't tell you anything about nesting. we use
+ // these when we run out of space in the GUID to store the path.
+
+ MultiByte1 = 0xC, // 1 byte follows. If this Nibble is in the high bits, it the high bits of the number are stored in the low nibble.
+ // commented out because the code does not explicitly reference the names (but they are logically defined).
+ // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimization)
+ // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimization)
+ // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimization)
+ }
+
+ /// Add the activity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId'
+ /// Thus if this number is 6 that is where 'id' will be added. This will return 13 (12
+ /// is the maximum number of bytes that fit in a GUID) if the path did not fit.
+ /// If 'overflow' is true, then the number is encoded as an 'overflow number (which has a
+ /// special (longer prefix) that indicates that this ID is allocated differently
+ [System.Security.SecurityCritical]
+ private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, bool overflow = false)
+ {
+ byte* ptr = (byte*)outPtr;
+ byte* endPtr = ptr + 12;
+ ptr += whereToAddId;
+ if (endPtr <= ptr)
+ return 13; // 12 means we might exactly fit, 13 means we definately did not fit
+
+ if (0 < id && id <= (uint)NumberListCodes.LastImmediateValue && !overflow)
+ WriteNibble(ref ptr, endPtr, id);
+ else
+ {
+ uint len = 4;
+ if (id <= 0xFF)
+ len = 1;
+ else if (id <= 0xFFFF)
+ len = 2;
+ else if (id <= 0xFFFFFF)
+ len = 3;
+
+ if (overflow)
+ {
+ if (endPtr <= ptr + 2) // I need at least 2 bytes
+ return 13;
+
+ // Write out the prefix code nibble and the length nibble
+ WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.PrefixCode);
+ }
+ // The rest is the same for overflow and non-overflow case
+ WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.MultiByte1 + (len - 1));
+
+ // Do we have an odd nibble? If so flush it or use it for the 12 byte case.
+ if (ptr < endPtr && *ptr != 0)
+ {
+ // If the value < 4096 we can use the nibble we are otherwise just outputting as padding.
+ if (id < 4096)
+ {
+ // Indicate this is a 1 byte multicode with 4 high order bits in the lower nibble.
+ *ptr = (byte)(((uint)NumberListCodes.MultiByte1 << 4) + (id >> 8));
+ id &= 0xFF; // Now we only want the low order bits.
+ }
+ ptr++;
+ }
+
+ // Write out the bytes.
+ while (0 < len)
+ {
+ if (endPtr <= ptr)
+ {
+ ptr++; // Indicate that we have overflowed
+ break;
+ }
+ *ptr++ = (byte)id;
+ id = (id >> 8);
+ --len;
+ }
+ }
+
+ // Compute the checksum
+ uint* sumPtr = (uint*)outPtr;
+ // We set the last DWORD the sum of the first 3 DWORDS in the GUID. This
+ sumPtr[3] = sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD; // This last number is a random number (it identifies us as us)
+
+ return (int)(ptr - ((byte*)outPtr));
+ }
+
+ /// <summary>
+ /// Write a single Nible 'value' (must be 0-15) to the byte buffer represented by *ptr.
+ /// Will not go past 'endPtr'. Also it assumes that we never write 0 so we can detect
+ /// whether a nibble has already been written to ptr because it will be nonzero.
+ /// Thus if it is non-zero it adds to the current byte, otherwise it advances and writes
+ /// the new byte (in the high bits) of the next byte.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value)
+ {
+ Contract.Assert(0 <= value && value < 16);
+ Contract.Assert(ptr < endPtr);
+
+ if (*ptr != 0)
+ *ptr++ |= (byte)value;
+ else
+ *ptr = (byte)(value << 4);
+ }
+
+ #endregion // CreateGuidForActivityPath
+
+ readonly internal string m_name; // The name used in the 'start' and 'stop' APIs to help match up
+ readonly long m_uniqueId; // a small number that makes this activity unique among its siblings
+ internal readonly Guid m_guid; // Activity Guid, it is basically an encoding of the Path() (see CreateActivityPathGuid)
+ internal readonly int m_activityPathGuidOffset; // Keeps track of where in m_guid the causality path stops (used to generated child GUIDs)
+ internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control)
+ readonly internal EventActivityOptions m_eventOptions; // Options passed to start.
+ internal long m_lastChildID; // used to create a unique ID for my children activities
+ internal int m_stopped; // This work item has stopped
+ readonly internal ActivityInfo m_creator; // My parent (creator). Forms the Path() for the activity.
+ readonly internal Guid m_activityIdToRestore; // The Guid to restore after a stop.
+ #endregion
+ }
+
+ // This callback is used to initialize the m_current AsyncLocal Variable.
+ // Its job is to keep the ETW Activity ID (part of thread local storage) in sync
+ // with m_current.ActivityID
+ void ActivityChanging(AsyncLocalValueChangedArgs<ActivityInfo> args)
+ {
+ ActivityInfo cur = args.CurrentValue;
+ ActivityInfo prev = args.PreviousValue;
+
+ // Are we popping off a value? (we have a prev, and it creator is cur)
+ // Then check if we should use the GUID at the time of the start event
+ if (prev != null && prev.m_creator == cur)
+ {
+ // If the saved activity ID is not the same as the creator activity
+ // that takes precedence (it means someone explicitly did a SetActivityID)
+ // Set it to that and get out
+ if (cur == null || prev.m_activityIdToRestore != cur.ActivityId)
+ {
+ EventSource.SetCurrentThreadActivityId(prev.m_activityIdToRestore);
+ return;
+ }
+ }
+
+ // OK we did not have an explicit SetActivityID set. Then we should be
+ // setting the activity to current ActivityInfo. However that activity
+ // might be dead, in which case we should skip it, so we never set
+ // the ID to dead things.
+ while (cur != null)
+ {
+ // We found a live activity (typically the first time), set it to that.
+ if (cur.m_stopped == 0)
+ {
+ EventSource.SetCurrentThreadActivityId(cur.ActivityId);
+ return;
+ }
+ cur = cur.m_creator;
+ }
+ // we can get here if there is no information on our activity stack (everything is dead)
+ // currently we do nothing, as that seems better than setting to Guid.Emtpy.
+ }
+
+ /// <summary>
+ /// Async local variables have the property that the are automatically copied whenever a task is created and used
+ /// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that
+ /// last set it.
+ ///
+ /// This variable points a a linked list that represents all Activities that have started but have not stopped.
+ /// </summary>
+ AsyncLocal<ActivityInfo> m_current;
+ bool m_checkedForEnable;
+
+ // Singleton
+ private static ActivityTracker s_activityTrackerInstance = new ActivityTracker();
+
+ // Used to create unique IDs at the top level. Not used for nested Ids (each activity has its own id generator)
+ static long m_nextId = 0;
+ private const ushort MAX_ACTIVITY_DEPTH = 100; // Limit maximum depth of activities to be tracked at 100.
+ // This will avoid leaking memory in case of activities that are never stopped.
+
+ #endregion
+ }
+
+#if ES_BUILD_STANDALONE || PROJECTN
+ /******************************** SUPPORT *****************************/
+ /// <summary>
+ /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created
+ /// by the current task. Thus all causally related code gets this value. Note that reads and writes to this VARIABLE
+ /// (not what it points it) to this does not need to be protected by locks because it is inherently thread local (you always
+ /// only get your thread local copy which means that you never have races.
+ /// </summary>
+ ///
+#if ES_BUILD_STANDALONE
+ [EventSource(Name = "Microsoft.Tasks.Nuget")]
+#else
+ [EventSource(Name = "System.Diagnostics.Tracing.TplEtwProvider")]
+#endif
+ internal class TplEtwProvider : EventSource
+ {
+ public class Keywords
+ {
+ public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80;
+ public const EventKeywords Debug = (EventKeywords)0x20000;
+ }
+
+ public static TplEtwProvider Log = new TplEtwProvider();
+ public bool Debug { get { return IsEnabled(EventLevel.Verbose, Keywords.Debug); } }
+
+ public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(1, Facility, Message); }
+ public void DebugFacilityMessage1(string Facility, string Message, string Arg) { WriteEvent(2, Facility, Message, Arg); }
+ public void SetActivityId(Guid Id) { WriteEvent(3, Id); }
+ }
+#endif
+
+#if ES_BUILD_AGAINST_DOTNET_V35 || ES_BUILD_PCL || NO_ASYNC_LOCAL
+ // In these cases we don't have any Async local support. Do nothing.
+ internal sealed class AsyncLocalValueChangedArgs<T>
+ {
+ public T PreviousValue { get { return default(T); } }
+ public T CurrentValue { get { return default(T); } }
+
+ }
+
+ internal sealed class AsyncLocal<T>
+ {
+ public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) {
+ throw new NotImplementedException("AsyncLocal only available on V4.6 and above");
+ }
+ public T Value
+ {
+ get { return default(T); }
+ set { }
+ }
+ }
+#endif
+
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventActivityOptions.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventActivityOptions.cs
new file mode 100644
index 0000000000..782afbf869
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventActivityOptions.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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// EventActivityOptions flags allow to specify different activity related characteristics.
+ /// </summary>
+ [Flags]
+ public enum EventActivityOptions
+ {
+ /// <summary>
+ /// No special options are added to the event.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Disable Implicit Activity Tracking
+ /// </summary>
+ Disable = 0x2,
+
+ /// <summary>
+ /// Allow activity event to call itself (directly or indirectly)
+ /// </summary>
+ Recursive = 0x4,
+
+ /// <summary>
+ /// Allows event activity to live beyond its parent.
+ /// </summary>
+ Detachable = 0x8
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs
new file mode 100644
index 0000000000..b1f946464e
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs
@@ -0,0 +1,436 @@
+// 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;
+using System.Threading;
+#if ES_BUILD_PCL
+ using System.Threading.Tasks;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Provides the ability to collect statistics through EventSource
+ /// </summary>
+ public class EventCounter
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EventCounter"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="eventSource">The event source.</param>
+ public EventCounter(string name, EventSource eventSource)
+ {
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+
+ InitializeBuffer();
+ _name = name;
+ EventCounterGroup.AddEventCounter(eventSource, this);
+ }
+
+ /// <summary>
+ /// Writes the metric.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ public void WriteMetric(float value)
+ {
+ Enqueue(value);
+ }
+
+ #region private implementation
+
+ private readonly string _name;
+
+ #region Buffer Management
+
+ // Values buffering
+ private const int BufferedSize = 10;
+ private const float UnusedBufferSlotValue = float.NegativeInfinity;
+ private const int UnsetIndex = -1;
+ private volatile float[] _bufferedValues;
+ private volatile int _bufferedValuesIndex;
+
+ private void InitializeBuffer()
+ {
+ _bufferedValues = new float[BufferedSize];
+ for (int i = 0; i < _bufferedValues.Length; i++)
+ {
+ _bufferedValues[i] = UnusedBufferSlotValue;
+ }
+ }
+
+ private void Enqueue(float value)
+ {
+ // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay.
+ int i = _bufferedValuesIndex;
+ while (true)
+ {
+ float result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue);
+ i++;
+ if (_bufferedValues.Length <= i)
+ {
+ // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other
+ // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too.
+ lock (_bufferedValues)
+ {
+ Flush();
+ }
+ i = 0;
+ }
+
+ if (result == UnusedBufferSlotValue)
+ {
+ // CompareExchange succeeded
+ _bufferedValuesIndex = i;
+ return;
+ }
+ }
+ }
+
+ private void Flush()
+ {
+ for (int i = 0; i < _bufferedValues.Length; i++)
+ {
+ var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue);
+ if (value != UnusedBufferSlotValue)
+ {
+ OnMetricWritten(value);
+ }
+ }
+
+ _bufferedValuesIndex = 0;
+ }
+
+ #endregion // Buffer Management
+
+ #region Statistics Calculation
+
+ // Statistics
+ private int _count;
+ private float _sum;
+ private float _sumSquared;
+ private float _min;
+ private float _max;
+
+ private void OnMetricWritten(float value)
+ {
+ _sum += value;
+ _sumSquared += value * value;
+ if (_count == 0 || value > _max)
+ {
+ _max = value;
+ }
+
+ if (_count == 0 || value < _min)
+ {
+ _min = value;
+ }
+
+ _count++;
+ }
+
+ internal EventCounterPayload GetEventCounterPayload()
+ {
+ lock (_bufferedValues)
+ {
+ Flush();
+ EventCounterPayload result = new EventCounterPayload();
+ result.Name = _name;
+ result.Count = _count;
+ result.Mean = _sum / _count;
+ result.StandardDerivation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count);
+ result.Min = _min;
+ result.Max = _max;
+ ResetStatistics();
+ return result;
+ }
+ }
+
+ private void ResetStatistics()
+ {
+ _count = 0;
+ _sum = 0;
+ _sumSquared = 0;
+ _min = 0;
+ _max = 0;
+ }
+
+ #endregion // Statistics Calculation
+
+ #endregion // private implementation
+ }
+
+ #region internal supporting classes
+
+ [EventData]
+ internal class EventCounterPayload : IEnumerable<KeyValuePair<string, object>>
+ {
+ public string Name { get; set; }
+
+ public float Mean { get; set; }
+
+ public float StandardDerivation { get; set; }
+
+ public int Count { get; set; }
+
+ public float Min { get; set; }
+
+ public float Max { get; set; }
+
+ public float IntervalSec { get; internal set; }
+
+ #region Implementation of the IEnumerable interface
+
+ public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+ {
+ return ForEnumeration.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ForEnumeration.GetEnumerator();
+ }
+
+ private IEnumerable<KeyValuePair<string, object>> ForEnumeration
+ {
+ get
+ {
+ yield return new KeyValuePair<string, object>("Name", Name);
+ yield return new KeyValuePair<string, object>("Mean", Mean);
+ yield return new KeyValuePair<string, object>("StandardDerivation", StandardDerivation);
+ yield return new KeyValuePair<string, object>("Count", Count);
+ yield return new KeyValuePair<string, object>("Min", Min);
+ yield return new KeyValuePair<string, object>("Max", Max);
+ }
+ }
+
+ #endregion // Implementation of the IEnumerable interface
+ }
+
+ internal class EventCounterGroup : IDisposable
+ {
+ private readonly EventSource _eventSource;
+ private readonly int _eventSourceIndex;
+ private readonly List<EventCounter> _eventCounters;
+
+ internal EventCounterGroup(EventSource eventSource, int eventSourceIndex)
+ {
+ _eventSource = eventSource;
+ _eventSourceIndex = eventSourceIndex;
+ _eventCounters = new List<EventCounter>();
+ RegisterCommandCallback();
+ }
+
+ private void Add(EventCounter eventCounter)
+ {
+ _eventCounters.Add(eventCounter);
+ }
+
+ #region EventSource Command Processing
+
+ private void RegisterCommandCallback()
+ {
+ _eventSource.EventCommandExecuted += OnEventSourceCommand;
+ }
+
+ private void OnEventSourceCommand(object sender, EventCommandEventArgs e)
+ {
+ if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update)
+ {
+ string valueStr;
+ float value;
+ if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value))
+ {
+ EnableTimer(value);
+ }
+ }
+ }
+
+ #endregion // EventSource Command Processing
+
+ #region Global EventCounterGroup Array management
+
+ private static EventCounterGroup[] s_eventCounterGroups;
+
+ internal static void AddEventCounter(EventSource eventSource, EventCounter eventCounter)
+ {
+ int eventSourceIndex = EventListener.EventSourceIndex(eventSource);
+
+ EventCounterGroup.EnsureEventSourceIndexAvailable(eventSourceIndex);
+ EventCounterGroup eventCounterGroup = GetEventCounterGroup(eventSource);
+ eventCounterGroup.Add(eventCounter);
+ }
+
+ private static void EnsureEventSourceIndexAvailable(int eventSourceIndex)
+ {
+ if (EventCounterGroup.s_eventCounterGroups == null)
+ {
+ EventCounterGroup.s_eventCounterGroups = new EventCounterGroup[eventSourceIndex + 1];
+ }
+ else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length)
+ {
+ EventCounterGroup[] newEventCounterGroups = new EventCounterGroup[eventSourceIndex + 1];
+ Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length);
+ EventCounterGroup.s_eventCounterGroups = newEventCounterGroups;
+ }
+ }
+
+ private static EventCounterGroup GetEventCounterGroup(EventSource eventSource)
+ {
+ int eventSourceIndex = EventListener.EventSourceIndex(eventSource);
+ EventCounterGroup result = EventCounterGroup.s_eventCounterGroups[eventSourceIndex];
+ if (result == null)
+ {
+ result = new EventCounterGroup(eventSource, eventSourceIndex);
+ EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = result;
+ }
+
+ return result;
+ }
+
+ #endregion // Global EventCounterGroup Array management
+
+ #region Timer Processing
+
+ private DateTime _timeStampSinceCollectionStarted;
+ private int _pollingIntervalInMilliseconds;
+ private Timer _pollingTimer;
+
+ private void EnableTimer(float pollingIntervalInSeconds)
+ {
+ if (pollingIntervalInSeconds == 0)
+ {
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+
+ _pollingIntervalInMilliseconds = 0;
+ }
+ else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds < _pollingIntervalInMilliseconds)
+ {
+ _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000);
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+
+ _timeStampSinceCollectionStarted = DateTime.Now;
+ _pollingTimer = new Timer(OnTimer, null, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds);
+ }
+ }
+
+ private void OnTimer(object state)
+ {
+ if (_eventSource.IsEnabled())
+ {
+ DateTime now = DateTime.Now;
+ TimeSpan elapsed = now - _timeStampSinceCollectionStarted;
+ lock (_pollingTimer)
+ {
+ foreach (var eventCounter in _eventCounters)
+ {
+ EventCounterPayload payload = eventCounter.GetEventCounterPayload();
+ payload.IntervalSec = (float)elapsed.TotalSeconds;
+ _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new { Payload = payload });
+ }
+
+
+ _timeStampSinceCollectionStarted = now;
+ }
+ }
+ else
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ EventCounterGroup.s_eventCounterGroups[_eventSourceIndex] = null;
+ }
+ }
+
+ #region PCL timer hack
+
+#if ES_BUILD_PCL
+ internal delegate void TimerCallback(object state);
+
+ internal sealed class Timer : CancellationTokenSource, IDisposable
+ {
+ private int _period;
+ private TimerCallback _callback;
+ private object _state;
+
+ internal Timer(TimerCallback callback, object state, int dueTime, int period)
+ {
+ _callback = callback;
+ _state = state;
+ _period = period;
+ Schedule(dueTime);
+ }
+
+ private void Schedule(int dueTime)
+ {
+ Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
+ }
+
+ private void OnTimer(Task t, object s)
+ {
+ Schedule(_period);
+ _callback(_state);
+ }
+
+ public new void Dispose() { base.Cancel(); }
+ }
+#endif
+ #endregion // PCL timer hack
+
+ #endregion // Timer Processing
+
+ #region Implementation of the IDisposable interface
+
+ private bool _disposed = false;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+ }
+
+ _disposed = true;
+ }
+
+ #endregion // Implementation of the IDisposable interface
+ }
+
+ #endregion // internal supporting classes
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs
new file mode 100644
index 0000000000..11b6e6bac2
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs
@@ -0,0 +1,193 @@
+// 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;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+#endif
+
+#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
+{
+ [StructLayout(LayoutKind.Explicit, Size = 16)]
+ [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
+ internal struct EventDescriptor
+ {
+ # region private
+ [FieldOffset(0)]
+ private int m_traceloggingId;
+ [FieldOffset(0)]
+ private ushort m_id;
+ [FieldOffset(2)]
+ private byte m_version;
+ [FieldOffset(3)]
+ private byte m_channel;
+ [FieldOffset(4)]
+ private byte m_level;
+ [FieldOffset(5)]
+ private byte m_opcode;
+ [FieldOffset(6)]
+ private ushort m_task;
+ [FieldOffset(8)]
+ private long m_keywords;
+ #endregion
+
+ public EventDescriptor(
+ int traceloggingId,
+ byte level,
+ byte opcode,
+ long keywords
+ )
+ {
+ this.m_id = 0;
+ this.m_version = 0;
+ this.m_channel = 0;
+ this.m_traceloggingId = traceloggingId;
+ this.m_level = level;
+ this.m_opcode = opcode;
+ this.m_task = 0;
+ this.m_keywords = keywords;
+ }
+
+ public EventDescriptor(
+ int id,
+ byte version,
+ byte channel,
+ byte level,
+ byte opcode,
+ int task,
+ long keywords
+ )
+ {
+ if (id < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (id > ushort.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue));
+ }
+
+ m_traceloggingId = 0;
+ m_id = (ushort)id;
+ m_version = version;
+ m_channel = channel;
+ m_level = level;
+ m_opcode = opcode;
+ m_keywords = keywords;
+
+ if (task < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (task > ushort.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue));
+ }
+
+ m_task = (ushort)task;
+ }
+
+ public int EventId
+ {
+ get
+ {
+ return m_id;
+ }
+ }
+ public byte Version
+ {
+ get
+ {
+ return m_version;
+ }
+ }
+ public byte Channel
+ {
+ get
+ {
+ return m_channel;
+ }
+ }
+ public byte Level
+ {
+ get
+ {
+ return m_level;
+ }
+ }
+ public byte Opcode
+ {
+ get
+ {
+ return m_opcode;
+ }
+ }
+ public int Task
+ {
+ get
+ {
+ return m_task;
+ }
+ }
+ public long Keywords
+ {
+ get
+ {
+ return m_keywords;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is EventDescriptor))
+ return false;
+
+ return Equals((EventDescriptor) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return m_id ^ m_version ^ m_channel ^ m_level ^ m_opcode ^ m_task ^ (int)m_keywords;
+ }
+
+ public bool Equals(EventDescriptor other)
+ {
+ if ((m_id != other.m_id) ||
+ (m_version != other.m_version) ||
+ (m_channel != other.m_channel) ||
+ (m_level != other.m_level) ||
+ (m_opcode != other.m_opcode) ||
+ (m_task != other.m_task) ||
+ (m_keywords != other.m_keywords))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static bool operator ==(EventDescriptor event1, EventDescriptor event2)
+ {
+ return event1.Equals(event2);
+ }
+
+ public static bool operator !=(EventDescriptor event1, EventDescriptor event2)
+ {
+ return !event1.Equals(event2);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs
new file mode 100644
index 0000000000..6ea8d98d92
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs
@@ -0,0 +1,1220 @@
+// 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 Microsoft.Win32;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
+using System.Threading;
+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_AGAINST_DOTNET_V35
+using Microsoft.Internal; // for Tuple (can't define alias for open generic types so we "use" the whole namespace)
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ // New in CLR4.0
+ internal enum ControllerCommand
+ {
+ // Strictly Positive numbers are for provider-specific commands, negative number are for 'shared' commands. 256
+ // The first 256 negative numbers are reserved for the framework.
+ Update = 0, // Not used by EventPrividerBase.
+ SendManifest = -1,
+ Enable = -2,
+ Disable = -3,
+ };
+
+ /// <summary>
+ /// Only here because System.Diagnostics.EventProvider needs one more extensibility hook (when it gets a
+ /// controller callback)
+ /// </summary>
+ [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
+ internal partial class EventProvider : IDisposable
+ {
+ // This is the windows EVENT_DATA_DESCRIPTOR structure. We expose it because this is what
+ // subclasses of EventProvider use when creating efficient (but unsafe) version of
+ // EventWrite. We do make it a nested type because we really don't expect anyone to use
+ // it except subclasses (and then only rarely).
+ public struct EventData
+ {
+ internal unsafe ulong Ptr;
+ internal uint Size;
+ internal uint Reserved;
+ }
+
+ /// <summary>
+ /// A struct characterizing ETW sessions (identified by the etwSessionId) as
+ /// activity-tracing-aware or legacy. A session that's activity-tracing-aware
+ /// has specified one non-zero bit in the reserved range 44-47 in the
+ /// 'allKeywords' value it passed in for a specific EventProvider.
+ /// </summary>
+ public struct SessionInfo
+ {
+ internal int sessionIdBit; // the index of the bit used for tracing in the "reserved" field of AllKeywords
+ internal int etwSessionId; // the machine-wide ETW session ID
+
+ internal SessionInfo(int sessionIdBit_, int etwSessionId_)
+ { sessionIdBit = sessionIdBit_; etwSessionId = etwSessionId_; }
+ }
+
+ private static bool m_setInformationMissing;
+
+ [SecurityCritical]
+ UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback; // Trace Callback function
+ private long m_regHandle; // Trace Registration Handle
+ private byte m_level; // Tracing Level
+ private long m_anyKeywordMask; // Trace Enable Flags
+ private long m_allKeywordMask; // Match all keyword
+ private List<SessionInfo> m_liveSessions; // current live sessions (Tuple<sessionIdBit, etwSessionId>)
+ private bool m_enabled; // Enabled flag from Trace callback
+ private Guid m_providerId; // Control Guid
+ internal bool m_disposed; // when true provider has unregistered
+
+ [ThreadStatic]
+ private static WriteEventErrorCode s_returnCode; // The last return code
+
+ private const int s_basicTypeAllocationBufferSize = 16;
+ private const int s_etwMaxNumberArguments = 128;
+ private const int s_etwAPIMaxRefObjCount = 8;
+ private const int s_maxEventDataDescriptors = 128;
+ private const int s_traceEventMaximumSize = 65482;
+ private const int s_traceEventMaximumStringSize = 32724;
+
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public enum WriteEventErrorCode : int
+ {
+ //check mapping to runtime codes
+ NoError = 0,
+ NoFreeBuffers = 1,
+ EventTooBig = 2,
+ NullInput = 3,
+ TooManyArgs = 4,
+ Other = 5,
+ };
+
+ // Because callbacks happen on registration, and we need the callbacks for those setup
+ // we can't call Register in the constructor.
+ //
+ // Note that EventProvider should ONLY be used by EventSource. In particular because
+ // it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise
+ // you may get native callbacks during shutdown when we have destroyed the delegate.
+ // EventSource has special logic to do this, no one else should be calling EventProvider.
+ internal EventProvider()
+ {
+ }
+
+ /// <summary>
+ /// This method registers the controlGuid of this class with ETW. We need to be running on
+ /// Vista or above. If not a PlatformNotSupported exception will be thrown. If for some
+ /// reason the ETW Register call failed a NotSupported exception will be thrown.
+ /// </summary>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventRegister(System.Guid&,Microsoft.Win32.UnsafeNativeMethods.ManifestEtw+EtwEnableCallback,System.Void*,System.Int64&):System.UInt32" />
+ // <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" />
+ // <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" />
+ // </SecurityKernel>
+ [System.Security.SecurityCritical]
+ internal unsafe void Register(Guid providerGuid)
+ {
+ m_providerId = providerGuid;
+ uint status;
+ m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack);
+
+ status = EventRegister(ref m_providerId, m_etwCallback);
+ if (status != 0)
+ {
+ throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status)));
+ }
+ }
+
+ //
+ // implement Dispose Pattern to early deregister from ETW insted of waiting for
+ // the finalizer to call deregistration.
+ // Once the user is done with the provider it needs to call Close() or Dispose()
+ // If neither are called the finalizer will unregister the provider anyway
+ //
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // <SecurityKernel Critical="True" TreatAsSafe="Does not expose critical resource" Ring="1">
+ // <ReferencesCritical Name="Method: Deregister():Void" Ring="1" />
+ // </SecurityKernel>
+ [System.Security.SecuritySafeCritical]
+ protected virtual void Dispose(bool disposing)
+ {
+ //
+ // explicit cleanup is done by calling Dispose with true from
+ // Dispose() or Close(). The disposing arguement is ignored because there
+ // are no unmanaged resources.
+ // The finalizer calls Dispose with false.
+ //
+
+ //
+ // check if the object has been allready disposed
+ //
+ if (m_disposed) return;
+
+ // Disable the provider.
+ m_enabled = false;
+
+ // Do most of the work under a lock to avoid shutdown race.
+ lock (EventListener.EventListenersLock)
+ {
+ // Double check
+ if (m_disposed)
+ return;
+
+ Deregister();
+ m_disposed = true;
+ }
+ }
+
+ /// <summary>
+ /// This method deregisters the controlGuid of this class with ETW.
+ ///
+ /// </summary>
+ public virtual void Close()
+ {
+ Dispose();
+ }
+
+ ~EventProvider()
+ {
+ Dispose(false);
+ }
+
+ /// <summary>
+ /// This method un-registers from ETW.
+ /// </summary>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventUnregister(System.Int64):System.Int32" />
+ // </SecurityKernel>
+ // TODO Check return code from UnsafeNativeMethods.ManifestEtw.EventUnregister
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Win32.UnsafeNativeMethods.ManifestEtw.EventUnregister(System.Int64)"), System.Security.SecurityCritical]
+ private unsafe void Deregister()
+ {
+ //
+ // Unregister from ETW using the RegHandle saved from
+ // the register call.
+ //
+
+ if (m_regHandle != 0)
+ {
+ EventUnregister();
+ m_regHandle = 0;
+ }
+ }
+
+ // <SecurityKernel Critical="True" Ring="0">
+ // <UsesUnsafeCode Name="Parameter filterData of type: Void*" />
+ // <UsesUnsafeCode Name="Parameter callbackContext of type: Void*" />
+ // </SecurityKernel>
+ [System.Security.SecurityCritical]
+ unsafe void EtwEnableCallBack(
+ [In] ref System.Guid sourceId,
+ [In] int controlCode,
+ [In] byte setLevel,
+ [In] long anyKeyword,
+ [In] long allKeyword,
+ [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData,
+ [In] void* callbackContext
+ )
+ {
+ // This is an optional callback API. We will therefore ignore any failures that happen as a
+ // result of turning on this provider as to not crash the app.
+ // EventSource has code to validate whether initialization it expected to occur actually occurred
+ try
+ {
+ ControllerCommand command = ControllerCommand.Update;
+ IDictionary<string, string> args = null;
+ bool skipFinalOnControllerCommand = false;
+ if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_ENABLE_PROVIDER)
+ {
+ m_enabled = true;
+ m_level = setLevel;
+ m_anyKeywordMask = anyKeyword;
+ m_allKeywordMask = allKeyword;
+
+ // ES_SESSION_INFO is a marker for additional places we #ifdeffed out to remove
+ // references to EnumerateTraceGuidsEx. This symbol is actually not used because
+ // today we use FEATURE_ACTIVITYSAMPLING to determine if this code is there or not.
+ // However we put it in the #if so that we don't lose the fact that this feature
+ // switch is at least partially independent of FEATURE_ACTIVITYSAMPLING
+
+ List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions();
+ foreach (var session in sessionsChanged)
+ {
+ int sessionChanged = session.Item1.sessionIdBit;
+ int etwSessionId = session.Item1.etwSessionId;
+ bool bEnabling = session.Item2;
+
+ skipFinalOnControllerCommand = true;
+ args = null; // reinitialize args for every session...
+
+ // if we get more than one session changed we have no way
+ // of knowing which one "filterData" belongs to
+ if (sessionsChanged.Count > 1)
+ filterData = null;
+
+ // read filter data only when a session is being *added*
+ byte[] data;
+ int keyIndex;
+ if (bEnabling &&
+ GetDataFromController(etwSessionId, filterData, out command, out data, out keyIndex))
+ {
+ args = new Dictionary<string, string>(4);
+ while (keyIndex < data.Length)
+ {
+ int keyEnd = FindNull(data, keyIndex);
+ int valueIdx = keyEnd + 1;
+ int valueEnd = FindNull(data, valueIdx);
+ if (valueEnd < data.Length)
+ {
+ string key = System.Text.Encoding.UTF8.GetString(data, keyIndex, keyEnd - keyIndex);
+ string value = System.Text.Encoding.UTF8.GetString(data, valueIdx, valueEnd - valueIdx);
+ args[key] = value;
+ }
+ keyIndex = valueEnd + 1;
+ }
+ }
+
+ // execute OnControllerCommand once for every session that has changed.
+ OnControllerCommand(command, args, (bEnabling ? sessionChanged : -sessionChanged), etwSessionId);
+ }
+ }
+ else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_DISABLE_PROVIDER)
+ {
+ m_enabled = false;
+ m_level = 0;
+ m_anyKeywordMask = 0;
+ m_allKeywordMask = 0;
+ m_liveSessions = null;
+ }
+ else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_CAPTURE_STATE)
+ {
+ command = ControllerCommand.SendManifest;
+ }
+ else
+ return; // per spec you ignore commands you don't recognize.
+
+ if (!skipFinalOnControllerCommand)
+ OnControllerCommand(command, args, 0, 0);
+ }
+ catch (Exception)
+ {
+ // We want to ignore any failures that happen as a result of turning on this provider as to
+ // not crash the app.
+ }
+ }
+
+ // New in CLR4.0
+ protected virtual void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments, int sessionId, int etwSessionId) { }
+ protected EventLevel Level { get { return (EventLevel)m_level; } set { m_level = (byte)value; } }
+ protected EventKeywords MatchAnyKeyword { get { return (EventKeywords)m_anyKeywordMask; } set { m_anyKeywordMask = unchecked((long)value); } }
+ protected EventKeywords MatchAllKeyword { get { return (EventKeywords)m_allKeywordMask; } set { m_allKeywordMask = unchecked((long)value); } }
+
+ static private int FindNull(byte[] buffer, int idx)
+ {
+ while (idx < buffer.Length && buffer[idx] != 0)
+ idx++;
+ return idx;
+ }
+
+ /// <summary>
+ /// Determines the ETW sessions that have been added and/or removed to the set of
+ /// sessions interested in the current provider. It does so by (1) enumerating over all
+ /// ETW sessions that enabled 'this.m_Guid' for the current process ID, and (2)
+ /// comparing the current list with a list it cached on the previous invocation.
+ ///
+ /// The return value is a list of tuples, where the SessionInfo specifies the
+ /// ETW session that was added or remove, and the bool specifies whether the
+ /// session was added or whether it was removed from the set.
+ /// </summary>
+ [System.Security.SecuritySafeCritical]
+ private List<Tuple<SessionInfo, bool>> GetSessions()
+ {
+ List<SessionInfo> liveSessionList = null;
+
+ GetSessionInfo((Action<int, long>)
+ ((etwSessionId, matchAllKeywords) =>
+ GetSessionInfoCallback(etwSessionId, matchAllKeywords, ref liveSessionList)));
+
+ List<Tuple<SessionInfo, bool>> changedSessionList = new List<Tuple<SessionInfo, bool>>();
+
+ // first look for sessions that have gone away (or have changed)
+ // (present in the m_liveSessions but not in the new liveSessionList)
+ if (m_liveSessions != null)
+ {
+ foreach (SessionInfo s in m_liveSessions)
+ {
+ int idx;
+ if ((idx = IndexOfSessionInList(liveSessionList, s.etwSessionId)) < 0 ||
+ (liveSessionList[idx].sessionIdBit != s.sessionIdBit))
+ changedSessionList.Add(Tuple.Create(s, false));
+
+ }
+ }
+ // next look for sessions that were created since the last callback (or have changed)
+ // (present in the new liveSessionList but not in m_liveSessions)
+ if (liveSessionList != null)
+ {
+ foreach (SessionInfo s in liveSessionList)
+ {
+ int idx;
+ if ((idx = IndexOfSessionInList(m_liveSessions, s.etwSessionId)) < 0 ||
+ (m_liveSessions[idx].sessionIdBit != s.sessionIdBit))
+ changedSessionList.Add(Tuple.Create(s, true));
+ }
+ }
+
+ m_liveSessions = liveSessionList;
+ return changedSessionList;
+ }
+
+
+ /// <summary>
+ /// This method is the callback used by GetSessions() when it calls into GetSessionInfo().
+ /// It updates a List{SessionInfo} based on the etwSessionId and matchAllKeywords that
+ /// GetSessionInfo() passes in.
+ /// </summary>
+ private static void GetSessionInfoCallback(int etwSessionId, long matchAllKeywords,
+ ref List<SessionInfo> sessionList)
+ {
+ uint sessionIdBitMask = (uint)SessionMask.FromEventKeywords(unchecked((ulong)matchAllKeywords));
+ // an ETW controller that specifies more than the mandated bit for our EventSource
+ // will be ignored...
+ if (bitcount(sessionIdBitMask) > 1)
+ return;
+
+ if (sessionList == null)
+ sessionList = new List<SessionInfo>(8);
+
+ if (bitcount(sessionIdBitMask) == 1)
+ {
+ // activity-tracing-aware etw session
+ sessionList.Add(new SessionInfo(bitindex(sessionIdBitMask) + 1, etwSessionId));
+ }
+ else
+ {
+ // legacy etw session
+ sessionList.Add(new SessionInfo(bitcount((uint)SessionMask.All) + 1, etwSessionId));
+ }
+ }
+
+ /// <summary>
+ /// This method enumerates over all active ETW sessions that have enabled 'this.m_Guid'
+ /// for the current process ID, calling 'action' for each session, and passing it the
+ /// ETW session and the 'AllKeywords' the session enabled for the current provider.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ private unsafe void GetSessionInfo(Action<int, long> action)
+ {
+ // We wish the EventSource package to be legal for Windows Store applications.
+ // Currently EnumerateTraceGuidsEx is not an allowed API, so we avoid its use here
+ // and use the information in the registry instead. This means that ETW controllers
+ // that do not publish their intent to the registry (basically all controllers EXCEPT
+ // TraceEventSesion) will not work properly
+
+ // However the framework version of EventSource DOES have ES_SESSION_INFO defined and thus
+ // does not have this issue.
+#if ES_SESSION_INFO || !ES_BUILD_STANDALONE
+ int buffSize = 256; // An initial guess that probably works most of the time.
+ byte* buffer;
+ for (; ; )
+ {
+ var space = stackalloc byte[buffSize];
+ buffer = space;
+ var hr = 0;
+
+ fixed (Guid* provider = &m_providerId)
+ {
+ hr = UnsafeNativeMethods.ManifestEtw.EnumerateTraceGuidsEx(UnsafeNativeMethods.ManifestEtw.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo,
+ provider, sizeof(Guid), buffer, buffSize, ref buffSize);
+ }
+ if (hr == 0)
+ break;
+ if (hr != 122 /* ERROR_INSUFFICIENT_BUFFER */)
+ return;
+ }
+
+ var providerInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_GUID_INFO*)buffer;
+ var providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1];
+ int processId = unchecked((int)Win32Native.GetCurrentProcessId());
+ // iterate over the instances of the EventProvider in all processes
+ for (int i = 0; i < providerInfos->InstanceCount; i++)
+ {
+ if (providerInstance->Pid == processId)
+ {
+ var enabledInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_ENABLE_INFO*)&providerInstance[1];
+ // iterate over the list of active ETW sessions "listening" to the current provider
+ for (int j = 0; j < providerInstance->EnableCount; j++)
+ action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword);
+ }
+ if (providerInstance->NextOffset == 0)
+ break;
+ Contract.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize);
+ var structBase = (byte*)providerInstance;
+ providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset];
+ }
+#else
+#if !ES_BUILD_PCL && !FEATURE_PAL // TODO command arguments don't work on PCL builds...
+ // This code is only used in the Nuget Package Version of EventSource. because
+ // the code above is using APIs baned from UWP apps.
+ //
+ // TODO: In addition to only working when TraceEventSession enables the provider, this code
+ // also has a problem because TraceEvent does not clean up if the registry is stale
+ // It is unclear if it is worth keeping, but for now we leave it as it does work
+ // at least some of the time.
+
+ // Determine our session from what is in the registry.
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
+ regKey = @"Software" + @"\Wow6432Node" + regKey;
+ else
+ regKey = @"Software" + regKey;
+
+ var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKey);
+ if (key != null)
+ {
+ foreach (string valueName in key.GetValueNames())
+ {
+ if (valueName.StartsWith("ControllerData_Session_", StringComparison.Ordinal))
+ {
+ string strId = valueName.Substring(23); // strip of the ControllerData_Session_
+ int etwSessionId;
+ if (int.TryParse(strId, out etwSessionId))
+ {
+ // we need to assert this permission for partial trust scenarios
+ (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert();
+ var data = key.GetValue(valueName) as byte[];
+ if (data != null)
+ {
+ var dataAsString = System.Text.Encoding.UTF8.GetString(data);
+ int keywordIdx = dataAsString.IndexOf("EtwSessionKeyword", StringComparison.Ordinal);
+ if (0 <= keywordIdx)
+ {
+ int startIdx = keywordIdx + 18;
+ int endIdx = dataAsString.IndexOf('\0', startIdx);
+ string keywordBitString = dataAsString.Substring(startIdx, endIdx-startIdx);
+ int keywordBit;
+ if (0 < endIdx && int.TryParse(keywordBitString, out keywordBit))
+ action(etwSessionId, 1L << keywordBit);
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+#endif
+ }
+
+ /// <summary>
+ /// Returns the index of the SesisonInfo from 'sessions' that has the specified 'etwSessionId'
+ /// or -1 if the value is not present.
+ /// </summary>
+ private static int IndexOfSessionInList(List<SessionInfo> sessions, int etwSessionId)
+ {
+ if (sessions == null)
+ return -1;
+ // for non-coreclr code we could use List<T>.FindIndex(Predicate<T>), but we need this to compile
+ // on coreclr as well
+ for (int i = 0; i < sessions.Count; ++i)
+ if (sessions[i].etwSessionId == etwSessionId)
+ return i;
+
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets any data to be passed from the controller to the provider. It starts with what is passed
+ /// into the callback, but unfortunately this data is only present for when the provider is active
+ /// at the time the controller issues the command. To allow for providers to activate after the
+ /// controller issued a command, we also check the registry and use that to get the data. The function
+ /// returns an array of bytes representing the data, the index into that byte array where the data
+ /// starts, and the command being issued associated with that data.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ private unsafe bool GetDataFromController(int etwSessionId,
+ UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, out ControllerCommand command, out byte[] data, out int dataStart)
+ {
+ data = null;
+ dataStart = 0;
+ if (filterData == null)
+ {
+#if (!ES_BUILD_PCL && !PROJECTN && !FEATURE_PAL)
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
+ regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey;
+ else
+ regKey = @"HKEY_LOCAL_MACHINE\Software" + regKey;
+
+ string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture);
+
+ // we need to assert this permission for partial trust scenarios
+ (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert();
+ data = Microsoft.Win32.Registry.GetValue(regKey, valueName, null) as byte[];
+ if (data != null)
+ {
+ // We only used the persisted data from the registry for updates.
+ command = ControllerCommand.Update;
+ return true;
+ }
+#endif
+ }
+ else
+ {
+ if (filterData->Ptr != 0 && 0 < filterData->Size && filterData->Size <= 1024)
+ {
+ data = new byte[filterData->Size];
+ Marshal.Copy((IntPtr)filterData->Ptr, data, 0, data.Length);
+ }
+ command = (ControllerCommand)filterData->Type;
+ return true;
+ }
+
+ command = ControllerCommand.Update;
+ return false;
+ }
+
+ /// <summary>
+ /// IsEnabled, method used to test if provider is enabled
+ /// </summary>
+ public bool IsEnabled()
+ {
+ return m_enabled;
+ }
+
+ /// <summary>
+ /// IsEnabled, method used to test if event is enabled
+ /// </summary>
+ /// <param name="level">
+ /// Level to test
+ /// </param>
+ /// <param name="keywords">
+ /// Keyword to test
+ /// </param>
+ public bool IsEnabled(byte level, long keywords)
+ {
+ //
+ // If not enabled at all, return false.
+ //
+ if (!m_enabled)
+ {
+ return false;
+ }
+
+ // This also covers the case of Level == 0.
+ if ((level <= m_level) ||
+ (m_level == 0))
+ {
+
+ //
+ // Check if Keyword is enabled
+ //
+
+ if ((keywords == 0) ||
+ (((keywords & m_anyKeywordMask) != 0) &&
+ ((keywords & m_allKeywordMask) == m_allKeywordMask)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ public static WriteEventErrorCode GetLastWriteEventError()
+ {
+ return s_returnCode;
+ }
+
+ //
+ // Helper function to set the last error on the thread
+ //
+ private static void SetLastError(int error)
+ {
+ switch (error)
+ {
+ case UnsafeNativeMethods.ManifestEtw.ERROR_ARITHMETIC_OVERFLOW:
+ case UnsafeNativeMethods.ManifestEtw.ERROR_MORE_DATA:
+ s_returnCode = WriteEventErrorCode.EventTooBig;
+ break;
+ case UnsafeNativeMethods.ManifestEtw.ERROR_NOT_ENOUGH_MEMORY:
+ s_returnCode = WriteEventErrorCode.NoFreeBuffers;
+ break;
+ }
+ }
+
+ // <SecurityKernel Critical="True" Ring="0">
+ // <UsesUnsafeCode Name="Local intptrPtr of type: IntPtr*" />
+ // <UsesUnsafeCode Name="Local intptrPtr of type: Int32*" />
+ // <UsesUnsafeCode Name="Local longptr of type: Int64*" />
+ // <UsesUnsafeCode Name="Local uintptr of type: UInt32*" />
+ // <UsesUnsafeCode Name="Local ulongptr of type: UInt64*" />
+ // <UsesUnsafeCode Name="Local charptr of type: Char*" />
+ // <UsesUnsafeCode Name="Local byteptr of type: Byte*" />
+ // <UsesUnsafeCode Name="Local shortptr of type: Int16*" />
+ // <UsesUnsafeCode Name="Local sbyteptr of type: SByte*" />
+ // <UsesUnsafeCode Name="Local ushortptr of type: UInt16*" />
+ // <UsesUnsafeCode Name="Local floatptr of type: Single*" />
+ // <UsesUnsafeCode Name="Local doubleptr of type: Double*" />
+ // <UsesUnsafeCode Name="Local boolptr of type: Boolean*" />
+ // <UsesUnsafeCode Name="Local guidptr of type: Guid*" />
+ // <UsesUnsafeCode Name="Local decimalptr of type: Decimal*" />
+ // <UsesUnsafeCode Name="Local booleanptr of type: Boolean*" />
+ // <UsesUnsafeCode Name="Parameter dataDescriptor of type: EventData*" />
+ // <UsesUnsafeCode Name="Parameter dataBuffer of type: Byte*" />
+ // </SecurityKernel>
+ [System.Security.SecurityCritical]
+ private static unsafe object EncodeObject(ref object data, ref EventData* dataDescriptor, ref byte* dataBuffer, ref uint totalEventSize)
+ /*++
+
+ Routine Description:
+
+ This routine is used by WriteEvent to unbox the object type and
+ to fill the passed in ETW data descriptor.
+
+ Arguments:
+
+ data - argument to be decoded
+
+ dataDescriptor - pointer to the descriptor to be filled (updated to point to the next empty entry)
+
+ dataBuffer - storage buffer for storing user data, needed because cant get the address of the object
+ (updated to point to the next empty entry)
+
+ Return Value:
+
+ null if the object is a basic type other than string or byte[]. String otherwise
+
+ --*/
+ {
+ Again:
+ dataDescriptor->Reserved = 0;
+
+ string sRet = data as string;
+ byte[] blobRet = null;
+
+ if (sRet != null)
+ {
+ dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
+ }
+ else if ((blobRet = data as byte[]) != null)
+ {
+ // first store array length
+ *(int*)dataBuffer = blobRet.Length;
+ dataDescriptor->Ptr = (ulong)dataBuffer;
+ dataDescriptor->Size = 4;
+ totalEventSize += dataDescriptor->Size;
+
+ // then the array parameters
+ dataDescriptor++;
+ dataBuffer += s_basicTypeAllocationBufferSize;
+ dataDescriptor->Size = (uint)blobRet.Length;
+ }
+ else if (data is IntPtr)
+ {
+ dataDescriptor->Size = (uint)sizeof(IntPtr);
+ IntPtr* intptrPtr = (IntPtr*)dataBuffer;
+ *intptrPtr = (IntPtr)data;
+ dataDescriptor->Ptr = (ulong)intptrPtr;
+ }
+ else if (data is int)
+ {
+ dataDescriptor->Size = (uint)sizeof(int);
+ int* intptr = (int*)dataBuffer;
+ *intptr = (int)data;
+ dataDescriptor->Ptr = (ulong)intptr;
+ }
+ else if (data is long)
+ {
+ dataDescriptor->Size = (uint)sizeof(long);
+ long* longptr = (long*)dataBuffer;
+ *longptr = (long)data;
+ dataDescriptor->Ptr = (ulong)longptr;
+ }
+ else if (data is uint)
+ {
+ dataDescriptor->Size = (uint)sizeof(uint);
+ uint* uintptr = (uint*)dataBuffer;
+ *uintptr = (uint)data;
+ dataDescriptor->Ptr = (ulong)uintptr;
+ }
+ else if (data is UInt64)
+ {
+ dataDescriptor->Size = (uint)sizeof(ulong);
+ UInt64* ulongptr = (ulong*)dataBuffer;
+ *ulongptr = (ulong)data;
+ dataDescriptor->Ptr = (ulong)ulongptr;
+ }
+ else if (data is char)
+ {
+ dataDescriptor->Size = (uint)sizeof(char);
+ char* charptr = (char*)dataBuffer;
+ *charptr = (char)data;
+ dataDescriptor->Ptr = (ulong)charptr;
+ }
+ else if (data is byte)
+ {
+ dataDescriptor->Size = (uint)sizeof(byte);
+ byte* byteptr = (byte*)dataBuffer;
+ *byteptr = (byte)data;
+ dataDescriptor->Ptr = (ulong)byteptr;
+ }
+ else if (data is short)
+ {
+ dataDescriptor->Size = (uint)sizeof(short);
+ short* shortptr = (short*)dataBuffer;
+ *shortptr = (short)data;
+ dataDescriptor->Ptr = (ulong)shortptr;
+ }
+ else if (data is sbyte)
+ {
+ dataDescriptor->Size = (uint)sizeof(sbyte);
+ sbyte* sbyteptr = (sbyte*)dataBuffer;
+ *sbyteptr = (sbyte)data;
+ dataDescriptor->Ptr = (ulong)sbyteptr;
+ }
+ else if (data is ushort)
+ {
+ dataDescriptor->Size = (uint)sizeof(ushort);
+ ushort* ushortptr = (ushort*)dataBuffer;
+ *ushortptr = (ushort)data;
+ dataDescriptor->Ptr = (ulong)ushortptr;
+ }
+ else if (data is float)
+ {
+ dataDescriptor->Size = (uint)sizeof(float);
+ float* floatptr = (float*)dataBuffer;
+ *floatptr = (float)data;
+ dataDescriptor->Ptr = (ulong)floatptr;
+ }
+ else if (data is double)
+ {
+ dataDescriptor->Size = (uint)sizeof(double);
+ double* doubleptr = (double*)dataBuffer;
+ *doubleptr = (double)data;
+ dataDescriptor->Ptr = (ulong)doubleptr;
+ }
+ else if (data is bool)
+ {
+ // WIN32 Bool is 4 bytes
+ dataDescriptor->Size = 4;
+ int* intptr = (int*)dataBuffer;
+ if (((bool)data))
+ {
+ *intptr = 1;
+ }
+ else
+ {
+ *intptr = 0;
+ }
+ dataDescriptor->Ptr = (ulong)intptr;
+ }
+ else if (data is Guid)
+ {
+ dataDescriptor->Size = (uint)sizeof(Guid);
+ Guid* guidptr = (Guid*)dataBuffer;
+ *guidptr = (Guid)data;
+ dataDescriptor->Ptr = (ulong)guidptr;
+ }
+ else if (data is decimal)
+ {
+ dataDescriptor->Size = (uint)sizeof(decimal);
+ decimal* decimalptr = (decimal*)dataBuffer;
+ *decimalptr = (decimal)data;
+ dataDescriptor->Ptr = (ulong)decimalptr;
+ }
+ else if (data is DateTime)
+ {
+ const long UTCMinTicks = 504911232000000000;
+ long dateTimeTicks = 0;
+ // We cannot translate dates sooner than 1/1/1601 in UTC.
+ // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks
+ if (((DateTime)data).Ticks > UTCMinTicks)
+ dateTimeTicks = ((DateTime)data).ToFileTimeUtc();
+ dataDescriptor->Size = (uint)sizeof(long);
+ long* longptr = (long*)dataBuffer;
+ *longptr = dateTimeTicks;
+ dataDescriptor->Ptr = (ulong)longptr;
+ }
+ else
+ {
+ if (data is System.Enum)
+ {
+ Type underlyingType = Enum.GetUnderlyingType(data.GetType());
+ if (underlyingType == typeof(int))
+ {
+#if !ES_BUILD_PCL
+ data = ((IConvertible)data).ToInt32(null);
+#else
+ data = (int)data;
+#endif
+ goto Again;
+ }
+ else if (underlyingType == typeof(long))
+ {
+#if !ES_BUILD_PCL
+ data = ((IConvertible)data).ToInt64(null);
+#else
+ data = (long)data;
+#endif
+ goto Again;
+ }
+ }
+
+ // To our eyes, everything else is a just a string
+ if (data == null)
+ sRet = "";
+ else
+ sRet = data.ToString();
+ dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
+ }
+
+ totalEventSize += dataDescriptor->Size;
+
+ // advance buffers
+ dataDescriptor++;
+ dataBuffer += s_basicTypeAllocationBufferSize;
+
+ return (object)sRet ?? (object)blobRet;
+ }
+
+ /// <summary>
+ /// WriteEvent, method to write a parameters with event schema properties
+ /// </summary>
+ /// <param name="eventDescriptor">
+ /// Event Descriptor for this event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// childActivityID is marked as 'related' to the current activity ID.
+ /// </param>
+ /// <param name="eventPayload">
+ /// Payload for the ETW event.
+ /// </param>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
+ // <UsesUnsafeCode Name="Local dataBuffer of type: Byte*" />
+ // <UsesUnsafeCode Name="Local pdata of type: Char*" />
+ // <UsesUnsafeCode Name="Local userData of type: EventData*" />
+ // <UsesUnsafeCode Name="Local userDataPtr of type: EventData*" />
+ // <UsesUnsafeCode Name="Local currentBuffer of type: Byte*" />
+ // <UsesUnsafeCode Name="Local v0 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v1 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v2 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v3 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v4 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v5 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v6 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v7 of type: Char*" />
+ // <ReferencesCritical Name="Method: EncodeObject(Object&, EventData*, Byte*):String" Ring="1" />
+ // </SecurityKernel>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Performance-critical code")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ [System.Security.SecurityCritical]
+ internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, params object[] eventPayload)
+ {
+ int status = 0;
+
+ if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))
+ {
+ int argCount = 0;
+ unsafe
+ {
+ argCount = eventPayload.Length;
+
+ if (argCount > s_etwMaxNumberArguments)
+ {
+ s_returnCode = WriteEventErrorCode.TooManyArgs;
+ return false;
+ }
+
+ uint totalEventSize = 0;
+ int index;
+ int refObjIndex = 0;
+ List<int> refObjPosition = new List<int>(s_etwAPIMaxRefObjCount);
+ List<object> dataRefObj = new List<object>(s_etwAPIMaxRefObjCount);
+ EventData* userData = stackalloc EventData[2 * argCount];
+ EventData* userDataPtr = (EventData*)userData;
+ byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument
+ byte* currentBuffer = dataBuffer;
+
+ //
+ // The loop below goes through all the arguments and fills in the data
+ // descriptors. For strings save the location in the dataString array.
+ // Calculates the total size of the event by adding the data descriptor
+ // size value set in EncodeObject method.
+ //
+ bool hasNonStringRefArgs = false;
+ for (index = 0; index < eventPayload.Length; index++)
+ {
+ if (eventPayload[index] != null)
+ {
+ object supportedRefObj;
+ supportedRefObj = EncodeObject(ref eventPayload[index], ref userDataPtr, ref currentBuffer, ref totalEventSize);
+
+ if (supportedRefObj != null)
+ {
+ // EncodeObject advanced userDataPtr to the next empty slot
+ int idx = (int)(userDataPtr - userData - 1);
+ if (!(supportedRefObj is string))
+ {
+ if (eventPayload.Length + idx + 1 - index > s_etwMaxNumberArguments)
+ {
+ s_returnCode = WriteEventErrorCode.TooManyArgs;
+ return false;
+ }
+ hasNonStringRefArgs = true;
+ }
+ dataRefObj.Add(supportedRefObj);
+ refObjPosition.Add(idx);
+ refObjIndex++;
+ }
+ }
+ else
+ {
+ s_returnCode = WriteEventErrorCode.NullInput;
+ return false;
+ }
+ }
+
+ // update argCount based on actual number of arguments written to 'userData'
+ argCount = (int)(userDataPtr - userData);
+
+ if (totalEventSize > s_traceEventMaximumSize)
+ {
+ s_returnCode = WriteEventErrorCode.EventTooBig;
+ return false;
+ }
+
+ // the optimized path (using "fixed" instead of allocating pinned GCHandles
+ if (!hasNonStringRefArgs && (refObjIndex < s_etwAPIMaxRefObjCount))
+ {
+ // Fast path: at most 8 string arguments
+
+ // ensure we have at least s_etwAPIMaxStringCount in dataString, so that
+ // the "fixed" statement below works
+ while (refObjIndex < s_etwAPIMaxRefObjCount)
+ {
+ dataRefObj.Add(null);
+ ++refObjIndex;
+ }
+
+ //
+ // now fix any string arguments and set the pointer on the data descriptor
+ //
+ fixed (char* v0 = (string)dataRefObj[0], v1 = (string)dataRefObj[1], v2 = (string)dataRefObj[2], v3 = (string)dataRefObj[3],
+ v4 = (string)dataRefObj[4], v5 = (string)dataRefObj[5], v6 = (string)dataRefObj[6], v7 = (string)dataRefObj[7])
+ {
+ userDataPtr = (EventData*)userData;
+ if (dataRefObj[0] != null)
+ {
+ userDataPtr[refObjPosition[0]].Ptr = (ulong)v0;
+ }
+ if (dataRefObj[1] != null)
+ {
+ userDataPtr[refObjPosition[1]].Ptr = (ulong)v1;
+ }
+ if (dataRefObj[2] != null)
+ {
+ userDataPtr[refObjPosition[2]].Ptr = (ulong)v2;
+ }
+ if (dataRefObj[3] != null)
+ {
+ userDataPtr[refObjPosition[3]].Ptr = (ulong)v3;
+ }
+ if (dataRefObj[4] != null)
+ {
+ userDataPtr[refObjPosition[4]].Ptr = (ulong)v4;
+ }
+ if (dataRefObj[5] != null)
+ {
+ userDataPtr[refObjPosition[5]].Ptr = (ulong)v5;
+ }
+ if (dataRefObj[6] != null)
+ {
+ userDataPtr[refObjPosition[6]].Ptr = (ulong)v6;
+ }
+ if (dataRefObj[7] != null)
+ {
+ userDataPtr[refObjPosition[7]].Ptr = (ulong)v7;
+ }
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
+ }
+ }
+ else
+ {
+ // Slow path: use pinned handles
+ userDataPtr = (EventData*)userData;
+
+ GCHandle[] rgGCHandle = new GCHandle[refObjIndex];
+ for (int i = 0; i < refObjIndex; ++i)
+ {
+ // below we still use "fixed" to avoid taking dependency on the offset of the first field
+ // in the object (the way we would need to if we used GCHandle.AddrOfPinnedObject)
+ rgGCHandle[i] = GCHandle.Alloc(dataRefObj[i], GCHandleType.Pinned);
+ if (dataRefObj[i] is string)
+ {
+ fixed (char* p = (string)dataRefObj[i])
+ userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
+ }
+ else
+ {
+ fixed (byte* p = (byte[])dataRefObj[i])
+ userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
+ }
+ }
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
+
+ for (int i = 0; i < refObjIndex; ++i)
+ {
+ rgGCHandle[i].Free();
+ }
+ }
+ }
+ }
+
+ if (status != 0)
+ {
+ SetLastError((int)status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// WriteEvent, method to be used by generated code on a derived class
+ /// </summary>
+ /// <param name="eventDescriptor">
+ /// Event Descriptor for this event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// If this event is generating a child activity (WriteEventTransfer related activity) this is child activity
+ /// This can be null for events that do not generate a child activity.
+ /// </param>
+ /// <param name="dataCount">
+ /// number of event descriptors
+ /// </param>
+ /// <param name="data">
+ /// pointer do the event data
+ /// </param>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
+ // </SecurityKernel>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ [System.Security.SecurityCritical]
+ internal unsafe protected bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data)
+ {
+ if (childActivityID != null)
+ {
+ // activity transfers are supported only for events that specify the Send or Receive opcode
+ Contract.Assert((EventOpcode)eventDescriptor.Opcode == EventOpcode.Send ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Receive ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Start ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Stop);
+ }
+
+ int status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, dataCount, (EventData*)data);
+
+ if (status != 0)
+ {
+ SetLastError(status);
+ return false;
+ }
+ return true;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ [System.Security.SecurityCritical]
+ internal unsafe bool WriteEventRaw(
+ ref EventDescriptor eventDescriptor,
+ Guid* activityID,
+ Guid* relatedActivityID,
+ int dataCount,
+ IntPtr data)
+ {
+ int status;
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(
+ m_regHandle,
+ ref eventDescriptor,
+ activityID,
+ relatedActivityID,
+ dataCount,
+ (EventData*)data);
+
+ if (status != 0)
+ {
+ SetLastError(status);
+ return false;
+ }
+ return true;
+ }
+
+
+ // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work
+ // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available).
+ [SecurityCritical]
+ private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback)
+ {
+ m_providerId = providerId;
+ m_etwCallback = enableCallback;
+ return UnsafeNativeMethods.ManifestEtw.EventRegister(ref providerId, enableCallback, null, ref m_regHandle);
+ }
+
+ [SecurityCritical]
+ private uint EventUnregister()
+ {
+ uint status = UnsafeNativeMethods.ManifestEtw.EventUnregister(m_regHandle);
+ m_regHandle = 0;
+ return status;
+ }
+
+ static int[] nibblebits = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
+ private static int bitcount(uint n)
+ {
+ int count = 0;
+ for (; n != 0; n = n >> 4)
+ count += nibblebits[n & 0x0f];
+ return count;
+ }
+ private static int bitindex(uint n)
+ {
+ Contract.Assert(bitcount(n) == 1);
+ int idx = 0;
+ while ((n & (1 << idx)) == 0)
+ idx++;
+ return idx;
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs
new file mode 100644
index 0000000000..8c2edfdec2
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs
@@ -0,0 +1,6962 @@
+// 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_UNIX
+
+#define FEATURE_MANAGED_ETW
+
+#if !ES_BUILD_STANDALONE && !CORECLR && !PROJECTN
+#define FEATURE_ACTIVITYSAMPLING
+#endif // !ES_BUILD_STANDALONE
+
+#endif // !PLATFORM_UNIX
+
+#if ES_BUILD_STANDALONE
+#define FEATURE_MANAGED_ETW_CHANNELS
+// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+#endif
+
+/* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
+// DESIGN NOTES
+// Over the years EventSource has become more complex and so it is important to understand
+// the basic structure of the code to insure that it does not grow more complex.
+//
+// Basic Model
+//
+// PRINCIPLE: EventSource - ETW decoupling
+//
+// Conceptually and EventSouce is something takes event logging data from the source methods
+// To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
+// KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
+// we will call the EtwEventListener, that forwards commands from ETW to EventSources and
+// listeners to the EventSources and forwards on those events to ETW. THus the model should
+// be that you DON'T NEED ETW.
+//
+// Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
+// to it directly, but this can be VIEWED AS AN OPTIMIATION.
+//
+// Basic Event Data Flow:
+//
+// There are two ways for event Data to enter the system
+// 1) WriteEvent* and friends. This is called the 'contract' based approach because
+// you write a method per event which forms a contract that is know at compile time.
+// In this scheme each event is given an EVENTID (small integer). which is its identity
+// 2) Write<T> methods. This is called the 'dynamic' approach because new events
+// can be created on the fly. Event identity is determined by the event NAME, and these
+// are not quite as efficient at runtime since you have at least a hash table lookup
+// on every event write.
+//
+// EventSource-EventListener transfer fully support both ways of writing events (either contract
+// based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
+// types. It is suggested, however, that you use the contract based approach when the event scheme
+// is known at compile time (that is whenever possible). It is more efficient, but more importantly
+// it makes the contract very explicit, and centralizes all policy about logging. These are good
+// things. The Write<T> API is really meant for more ad-hoc
+//
+// Allowed Data.
+//
+// Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
+// during the transfer. In particular object identity is not preserved, some objects are morphed,
+// and not all data types are supported. In particular you can pass
+//
+// A Valid type to log to an EventSource include
+// * Primitive data types
+// * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
+// * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
+//
+// This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
+//
+// Explicitly allowed structs include (* New for V4.6)
+// * Marked with the EventData attribute
+// * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
+// * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
+//
+// When classes are returned in an EventListener, what is returned is something that implements
+// IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
+// into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
+// are obvious NOT the original objects.
+//
+// ETWserialization formats:
+//
+// As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
+// copy/morph of that data as described above. In addition the .NET framework supports a conceptual
+// ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
+// to be serialized in a way that ETW supports. ETW supports the following serialization formats
+//
+// 1) Manifest Based serialization.
+// 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
+//
+// A key factor is that the Write<T> method, which support on the fly definition of events, can't
+// support the manifest based serialization because the manifest needs the schema of all events
+// to be known before any events are emitted. This implies the following
+//
+// If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
+// If you use the EventSource(string) constructor for an eventSource (in which you don't
+// create a subclass), the default is also to use Self-Describing serialization. In addition
+// you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
+// Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
+//
+// Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
+//
+// *************************************************************************************
+// *** INTERNALS: Event Propagation
+//
+// Data enters the system either though
+//
+// 1) A user defined method in the user defined subclass of EventSource which calls
+// A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
+// * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
+// B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
+// C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
+//
+// All event data eventually flows to one of
+// * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
+// * WriteEventVarargs(ID, Guid*, object[])
+//
+// 2) A call to one of the overloads of Write<T>. All these overloads end up in
+// * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
+//
+// On output there are the following routines
+// Writing to all listeners that are NOT ETW, we have the following routines
+// * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
+// * WriteToAllListeners(ID, Guid*, object[])
+// * WriteToAllListeners(NAME, Guid*, EventPayload)
+//
+// EventPayload is the internal type that implements the IDictionary<string, object> interface
+// The EventListeners will pass back for serialized classes for nested object, but
+// WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
+// were parameters to a method.
+//
+// The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
+//
+// Writing to ETW, Manifest Based
+// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
+// EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
+// Writing to ETW, Self-Describing format
+// WriteMultiMerge(NAME, Options, Types, EventData*)
+// WriteMultiMerge(NAME, Options, Types, object[])
+// WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
+// will write it to
+//
+// All ETW writes eventually call
+// EventWriteTransfer (native PINVOKE wrapper)
+// EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
+// EventProvider.WriteEventRaw - sets last error
+// EventSource.WriteEventRaw - Does EventSource exception handling logic
+// WriteMultiMerge
+// WriteImpl<T>
+// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
+// EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
+//
+// Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
+// how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
+// since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
+// can call one of these
+// WriteMetadata - transforms the type T into serialization meta data blob for that type
+// WriteObjectData - transforms an object of T into serialization meta data blob for that type
+// GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
+// The first two are used to serialize something for ETW. The second one is used to transform the object
+// for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
+// deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
+//
+// It is an important observation that while EventSource does support users directly calling with EventData*
+// blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
+// path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
+//
+// TODO There is cleanup needed There should be no divergence until WriteEventRaw.
+//
+// TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
+// was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
+// fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
+// This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
+// opportunity to expose this format to EventListeners in the future.
+//
+using System;
+using System.Runtime.CompilerServices;
+#if FEATURE_ACTIVITYSAMPLING
+using System.Collections.Concurrent;
+#endif
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Reflection;
+using System.Resources;
+using System.Security;
+using System.Security.Permissions;
+
+using System.Text;
+using System.Threading;
+using Microsoft.Win32;
+
+#if ES_BUILD_STANDALONE
+using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
+#else
+using System.Threading.Tasks;
+#endif
+
+using Microsoft.Reflection;
+
+#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>
+ /// This class is meant to be inherited by a user-defined event source in order to define a managed
+ /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
+ /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
+ /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
+ /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
+ /// is sufficient for many users.
+ /// <para>
+ /// To achieve more control over the ETW provider manifest exposed by the event source type, the
+ /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
+ /// </para><para>
+ /// For very advanced EventSources, it is possible to intercept the commands being given to the
+ /// eventSource and change what filtering is done (see EventListener.EnableEvents and
+ /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
+ /// e.g. dumping a data structure (see EventSource.SendCommand and
+ /// <see cref="EventSource.OnEventCommand"/>).
+ /// </para><para>
+ /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
+ /// It is also possible to control and intercept the data dispatcher programmatically. See
+ /// <see cref="EventListener"/> for more.
+ /// </para>
+ /// </summary>
+ /// <remarks>
+ /// This is a minimal definition for a custom event source:
+ /// <code>
+ /// [EventSource(Name="Samples-Demos-Minimal")]
+ /// sealed class MinimalEventSource : EventSource
+ /// {
+ /// public static MinimalEventSource Log = new MinimalEventSource();
+ /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
+ /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
+ /// private MinimalEventSource() {}
+ /// }
+ /// </code>
+ /// </remarks>
+ public partial class EventSource : IDisposable
+ {
+
+#if FEATURE_EVENTSOURCE_XPLAT
+ private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
+#endif //FEATURE_EVENTSOURCE_XPLAT
+
+ /// <summary>
+ /// The human-friendly name of the eventSource. It defaults to the simple name of the class
+ /// </summary>
+ public string Name { get { return m_name; } }
+ /// <summary>
+ /// Every eventSource is assigned a GUID to uniquely identify it to the system.
+ /// </summary>
+ public Guid Guid { get { return m_guid; } }
+
+ /// <summary>
+ /// Returns true if the eventSource has been enabled at all. This is the prefered test
+ /// to be performed before a relatively expensive EventSource operation.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled()
+ {
+ return m_eventSourceEnabled;
+ }
+
+ /// <summary>
+ /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
+ ///
+ /// Note that the result of this function is only an approximation on whether a particular
+ /// event is active or not. It is only meant to be used as way of avoiding expensive
+ /// computation for logging when logging is not on, therefore it sometimes returns false
+ /// positives (but is always accurate when returning false). EventSources are free to
+ /// have additional filtering.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled(EventLevel level, EventKeywords keywords)
+ {
+ return IsEnabled(level, keywords, EventChannel.None);
+ }
+
+ /// <summary>
+ /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
+ /// if 'keywords' specifies a channel bit for a channel that is enabled.
+ ///
+ /// Note that the result of this function only an approximation on whether a particular
+ /// event is active or not. It is only meant to be used as way of avoiding expensive
+ /// computation for logging when logging is not on, therefore it sometimes returns false
+ /// positives (but is always accurate when returning false). EventSources are free to
+ /// have additional filtering.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
+ {
+ if (!m_eventSourceEnabled)
+ return false;
+
+ if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
+ return false;
+
+#if !FEATURE_ACTIVITYSAMPLING
+
+ return true;
+
+#else // FEATURE_ACTIVITYSAMPLING
+
+ return true;
+
+#if OPTIMIZE_IS_ENABLED
+ //================================================================================
+ // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
+ // in case activity tracing/sampling is enabled. The added complexity of this
+ // code however weighs against having it "on" until we know it's really needed.
+ // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
+ //================================================================================
+
+ // At this point we believe the event is enabled, however we now need to check
+ // if we filter because of activity
+
+ // Optimization, all activity filters also register a delegate here, so if there
+ // is no delegate, we know there are no activity filters, which means that there
+ // is no additional filtering, which means that we can return true immediately.
+ if (s_activityDying == null)
+ return true;
+
+ // if there's at least one legacy ETW listener we can't filter this
+ if (m_legacySessions != null && m_legacySessions.Count > 0)
+ return true;
+
+ // if any event ID that triggers a new activity, or "transfers" activities
+ // is covered by 'keywords' we can't filter this
+ if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
+ return true;
+
+ // See if all listeners have activity filters that would block the event.
+ for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
+ if (etwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ if (activityFilter == null ||
+ ActivityFilter.GetFilter(activityFilter, this) == null)
+ {
+ // No activity filter for ETW, if event is active for ETW, we can't filter.
+ for (int i = 0; i < m_eventData.Length; i++)
+ if (m_eventData[i].EnabledForETW)
+ return true;
+ }
+ else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
+ return true;
+ }
+
+ // for regular event listeners
+ var curDispatcher = m_Dispatchers;
+ while (curDispatcher != null)
+ {
+ ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
+ if (activityFilter == null)
+ {
+ // See if any event is enabled.
+ for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
+ if (curDispatcher.m_EventEnabled[i])
+ return true;
+ }
+ else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
+ return true;
+ curDispatcher = curDispatcher.m_Next;
+ }
+
+ // Every listener has an activity filter that is blocking writing the event,
+ // thus the event is not enabled.
+ return false;
+#endif // OPTIMIZE_IS_ENABLED
+
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+
+ /// <summary>
+ /// Returns the settings for the event source instance
+ /// </summary>
+ public EventSourceSettings Settings
+ {
+ get { return m_config; }
+ }
+
+ // Manifest support
+ /// <summary>
+ /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
+ /// This API allows you to compute this without actually creating an instance of the EventSource.
+ /// It only needs to reflect over the type.
+ /// </summary>
+ public static Guid GetGuid(Type eventSourceType)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
+ string name = eventSourceType.Name;
+ if (attrib != null)
+ {
+ if (attrib.Guid != null)
+ {
+ Guid g = Guid.Empty;
+#if !ES_BUILD_AGAINST_DOTNET_V35
+ if (Guid.TryParse(attrib.Guid, out g))
+ return g;
+#else
+ try { return new Guid(attrib.Guid); }
+ catch (Exception) { }
+#endif
+ }
+
+ if (attrib.Name != null)
+ name = attrib.Name;
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), nameof(eventSourceType));
+ }
+ return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
+ }
+ /// <summary>
+ /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
+ /// This API allows you to compute this without actually creating an instance of the EventSource.
+ /// It only needs to reflect over the type.
+ /// </summary>
+ public static string GetName(Type eventSourceType)
+ {
+ return GetName(eventSourceType, EventManifestOptions.None);
+ }
+
+ /// <summary>
+ /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
+ /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
+ /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
+ /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
+ /// </summary>
+ /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
+ /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
+ /// which it is embedded. This parameter specifies what name will be used</param>
+ /// <returns>The XML data string</returns>
+ public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
+ {
+ return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
+ }
+ /// <summary>
+ /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
+ /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
+ /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
+ /// ensures that the entries in the event log will be "optimally" localized.
+ /// </summary>
+ /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
+ /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
+ /// which it is embedded. This parameter specifies what name will be used</param>
+ /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
+ /// this returns null when the eventSourceType does not require explicit registration</param>
+ /// <returns>The XML data string or null</returns>
+ public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
+ return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
+ }
+
+ // EventListener support
+ /// <summary>
+ /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
+ /// </summary>
+ /// <returns></returns>
+ public static IEnumerable<EventSource> GetSources()
+ {
+ var ret = new List<EventSource>();
+ lock (EventListener.EventListenersLock)
+ {
+ foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null && !eventSource.IsDisposed)
+ ret.Add(eventSource);
+ }
+ }
+ return ret;
+ }
+
+ /// <summary>
+ /// Send a command to a particular EventSource identified by 'eventSource'.
+ /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
+ /// callback. What the EventSource does with the command and its arguments are from
+ /// that point EventSource-specific.
+ /// </summary>
+ /// <param name="eventSource">The instance of EventSource to send the command to</param>
+ /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
+ /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
+ public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
+ {
+ if (eventSource == null)
+ throw new ArgumentNullException(nameof(eventSource));
+
+ // User-defined EventCommands should not conflict with the reserved commands.
+ if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), nameof(command));
+ }
+
+ eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
+ }
+
+#if !ES_BUILD_STANDALONE
+ /// <summary>
+ /// This property allows EventSource code to appropriately handle as "different"
+ /// activities started on different threads that have not had an activity created on them.
+ /// </summary>
+ internal static Guid InternalCurrentThreadActivityId
+ {
+ [System.Security.SecurityCritical]
+ get
+ {
+ Guid retval = CurrentThreadActivityId;
+ if (retval == Guid.Empty)
+ {
+ retval = FallbackActivityId;
+ }
+ return retval;
+ }
+ }
+
+ internal static Guid FallbackActivityId
+ {
+ [System.Security.SecurityCritical]
+ get
+ {
+#pragma warning disable 612, 618
+ int threadID = AppDomain.GetCurrentThreadId();
+
+ // Managed thread IDs are more aggressively re-used than native thread IDs,
+ // so we'll use the latter...
+ return new Guid(unchecked((uint)threadID),
+ unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
+ 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
+#pragma warning restore 612, 618
+ }
+ }
+#endif // !ES_BUILD_STANDALONE
+
+ // Error APIs. (We don't throw by default, but you can probe for status)
+ /// <summary>
+ /// Because
+ ///
+ /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
+ /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
+ ///
+ /// The event source constructor does not throw exceptions. Instead we remember any exception that
+ /// was generated (it is also logged to Trace.WriteLine).
+ /// </summary>
+ public Exception ConstructionException { get { return m_constructionException; } }
+
+ /// <summary>
+ /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
+ /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
+ /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
+ /// construction time and can be retrieved by using this GetTrait API.
+ /// </summary>
+ /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
+ /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
+ public string GetTrait(string key)
+ {
+ if (m_traits != null)
+ {
+ for (int i = 0; i < m_traits.Length - 1; i += 2)
+ {
+ if (m_traits[i] == key)
+ return m_traits[i + 1];
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Displays the name and GUID for the eventSource for debugging purposes.
+ /// </summary>
+ public override string ToString()
+ {
+ return Resources.GetResourceString("EventSource_ToString", Name, Guid);
+ }
+
+ /// <summary>
+ /// Fires when a Command (e.g. Enable) comes from a an EventListener.
+ /// </summary>
+ public event EventHandler<EventCommandEventArgs> EventCommandExecuted
+ {
+ add
+ {
+ m_eventCommandExecuted += value;
+
+ // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
+ // It should get a chance to handle the deferred commands.
+ EventCommandEventArgs deferredCommands = m_deferredCommands;
+ while (deferredCommands != null)
+ {
+ value(this, deferredCommands);
+ deferredCommands = deferredCommands.nextCommand;
+ }
+ }
+ remove
+ {
+ m_eventCommandExecuted -= value;
+ }
+ }
+
+ #region protected
+ /// <summary>
+ /// This is the constructor that most users will use to create their eventSource. It takes
+ /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
+ /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
+ /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
+ /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
+ /// the ETW provider name.
+ /// </summary>
+ protected EventSource()
+ : this(EventSourceSettings.EtwManifestEventFormat)
+ {
+ }
+
+ /// <summary>
+ /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
+ /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
+ /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
+ /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
+ /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
+ /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
+ ///
+ /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
+ /// </summary>
+ // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
+ protected EventSource(bool throwOnEventWriteErrors)
+ : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
+ { }
+
+ /// <summary>
+ /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
+ /// </summary>
+ protected EventSource(EventSourceSettings settings) : this(settings, null) { }
+
+ /// <summary>
+ /// Construct an EventSource with additional non-default settings.
+ ///
+ /// 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="settings">See EventSourceSettings for more.</param>
+ /// <param name="traits">A collection of key-value strings (must be an even number).</param>
+ protected EventSource(EventSourceSettings settings, params string[] traits)
+ {
+ m_config = ValidateSettings(settings);
+
+ Guid eventSourceGuid;
+ string eventSourceName;
+
+ EventMetadata[] eventDescriptors;
+ byte[] manifest;
+ GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
+
+ if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
+ {
+ var myType = this.GetType();
+ eventSourceGuid = GetGuid(myType);
+ eventSourceName = GetName(myType);
+ }
+
+ Initialize(eventSourceGuid, eventSourceName, traits);
+ }
+
+ internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
+ {
+ //
+ // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
+ // On other architectures it is a no-op.
+ //
+ // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
+ // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
+ //
+ // This will be implemented by an IL rewriter, so we can't make this method abstract or the initial build of the subclass would fail.
+ //
+ eventSourceGuid = Guid.Empty;
+ eventSourceName = null;
+ eventData = null;
+ manifestBytes = null;
+
+ return;
+ }
+
+ /// <summary>
+ /// This method is called when the eventSource is updated by the controller.
+ /// </summary>
+ protected virtual void OnEventCommand(EventCommandEventArgs command) { }
+
+#pragma warning disable 1591
+ // optimized for common signatures (no args)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId)
+ {
+ WriteEventCore(eventId, 0, null);
+ }
+
+ // optimized for common signatures (ints)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+
+ // optimized for common signatures (longs)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 8;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+
+ // optimized for common signatures (strings)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ if (arg2 == null) arg2 = "";
+ fixed (char* string1Bytes = arg1)
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ if (arg2 == null) arg2 = "";
+ if (arg3 == null) arg3 = "";
+ fixed (char* string1Bytes = arg1)
+ fixed (char* string2Bytes = arg2)
+ fixed (char* string3Bytes = arg3)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[2].DataPointer = (IntPtr)string3Bytes;
+ descrs[2].Size = ((arg3.Length + 1) * 2);
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (string and ints)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (string and longs)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (long and string)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg2 == null) arg2 = "";
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (int and string)
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg2 == null) arg2 = "";
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, byte[] arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ if (arg1 == null || arg1.Length == 0)
+ {
+ int blobSize = 0;
+ descrs[0].DataPointer = (IntPtr)(&blobSize);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
+ descrs[1].Size = 0;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ else
+ {
+ int blobSize = arg1.Length;
+ fixed (byte* blob = &arg1[0])
+ {
+ descrs[0].DataPointer = (IntPtr)(&blobSize);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)blob;
+ descrs[1].Size = blobSize;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ if (arg2 == null || arg2.Length == 0)
+ {
+ int blobSize = 0;
+ descrs[1].DataPointer = (IntPtr)(&blobSize);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
+ descrs[2].Size = 0;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ else
+ {
+ int blobSize = arg2.Length;
+ fixed (byte* blob = &arg2[0])
+ {
+ descrs[1].DataPointer = (IntPtr)(&blobSize);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)blob;
+ descrs[2].Size = blobSize;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+ }
+
+#pragma warning restore 1591
+
+ /// <summary>
+ /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
+ /// </summary>
+ protected internal struct EventData
+ {
+ /// <summary>
+ /// Address where the one argument lives (if this points to managed memory you must ensure the
+ /// managed object is pinned.
+ /// </summary>
+ public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
+ /// <summary>
+ /// Size of the argument referenced by DataPointer
+ /// </summary>
+ public int Size { get { return m_Size; } set { m_Size = value; } }
+
+ #region private
+ /// <summary>
+ /// Initializes the members of this EventData object to point at a previously-pinned
+ /// tracelogging-compatible metadata blob.
+ /// </summary>
+ /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
+ /// <param name="size">The size of the metadata blob.</param>
+ /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
+ [SecurityCritical]
+ internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
+ {
+ this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
+ this.m_Size = size;
+ this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
+ }
+
+ //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
+ // the way EventWrite wants it.
+ internal long m_Ptr;
+ internal int m_Size;
+#pragma warning disable 0649
+ internal int m_Reserved; // Used to pad the size to match the Win32 API
+#pragma warning restore 0649
+ #endregion
+ }
+
+ /// <summary>
+ /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
+ /// do this, while straightforward, is unsafe.
+ /// </summary>
+ /// <remarks>
+ /// <code>
+ /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
+ /// {
+ /// if (IsEnabled())
+ /// {
+ /// if (arg2 == null) arg2 = "";
+ /// fixed (char* string2Bytes = arg2)
+ /// {
+ /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
+ /// descrs[0].Size = 8;
+ /// descrs[1].DataPointer = (IntPtr)string2Bytes;
+ /// descrs[1].Size = ((arg2.Length + 1) * 2);
+ /// WriteEventCore(eventId, 2, descrs);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </remarks>
+ [SecurityCritical]
+ [CLSCompliant(false)]
+ protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
+ {
+ WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
+ }
+
+ /// <summary>
+ /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
+ /// that you use to do this, while straightforward, is unsafe. The only difference from
+ /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
+ /// </summary>
+ /// <remarks>
+ /// <code>
+ /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
+ /// {
+ /// if (IsEnabled())
+ /// {
+ /// if (arg2 == null) arg2 = "";
+ /// fixed (char* string2Bytes = arg2)
+ /// {
+ /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
+ /// descrs[0].Size = 8;
+ /// descrs[1].DataPointer = (IntPtr)string2Bytes;
+ /// descrs[1].Size = ((arg2.Length + 1) * 2);
+ /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </remarks>
+ [SecurityCritical]
+ [CLSCompliant(false)]
+ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
+ {
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
+ if (relatedActivityId != null)
+ ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
+
+#if FEATURE_MANAGED_ETW
+ if (m_eventData[eventId].EnabledForETW)
+ {
+ EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
+ EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
+ Guid* pActivityId = null;
+ Guid activityId = Guid.Empty;
+ Guid relActivityId = Guid.Empty;
+
+ if (opcode != EventOpcode.Info && relatedActivityId == null &&
+ ((activityOptions & EventActivityOptions.Disable) == 0))
+ {
+ if (opcode == EventOpcode.Start)
+ {
+ m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
+ }
+ else if (opcode == EventOpcode.Stop)
+ {
+ m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
+ }
+
+ if (activityId != Guid.Empty)
+ pActivityId = &activityId;
+ if (relActivityId != Guid.Empty)
+ relatedActivityId = &relActivityId;
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ // this code should be kept in sync with WriteEventVarargs().
+ SessionMask etwSessions = SessionMask.All;
+ // only compute etwSessions if there are *any* ETW filters enabled...
+ if ((ulong)m_curLiveSessions != 0)
+ etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
+ // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
+ // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
+
+ if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
+ {
+ if (!SelfDescribingEvents)
+ {
+ if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
+ {
+ // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
+ // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
+ // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
+ // mask set to 0x0f so, when all ETW sessions want the event we don't need to
+ // synthesize a new one
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
+ // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
+ // only some of the ETW sessions will receive this event. Synthesize a new
+ // Descriptor whose Keywords field will have the appropriate bits set.
+ // etwSessions might be 0, if there are legacy ETW listeners that want this event
+ var desc = new EventDescriptor(
+ m_eventData[eventId].Descriptor.EventId,
+ m_eventData[eventId].Descriptor.Version,
+ m_eventData[eventId].Descriptor.Channel,
+ m_eventData[eventId].Descriptor.Level,
+ m_eventData[eventId].Descriptor.Opcode,
+ m_eventData[eventId].Descriptor.Task,
+ unchecked((long)etwSessions.ToEventKeywords() | origKwd));
+
+ if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
+ }
+ }
+#else
+ if (!SelfDescribingEvents)
+ {
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ m_eventData[eventId].Tags,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+#endif // FEATURE_MANAGED_ETW
+
+ if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
+ WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(m_eventData[eventId].Name, ex);
+ }
+ }
+ }
+
+ // fallback varags helpers.
+ /// <summary>
+ /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
+ /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
+ /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
+ /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
+ /// check so that the varargs call is not made when the EventSource is not active.
+ /// </summary>
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, params object[] args)
+ {
+ WriteEventVarargs(eventId, null, args);
+ }
+
+ /// <summary>
+ /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
+ /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
+ /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
+ /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
+ /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
+ /// check so that the varargs call is not made when the EventSource is not active.
+ /// </summary>
+ [SecuritySafeCritical]
+ protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
+ {
+ WriteEventVarargs(eventId, &relatedActivityId, args);
+ }
+
+ #endregion
+
+ #region IDisposable Members
+ /// <summary>
+ /// Disposes of an EventSource.
+ /// </summary>
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ /// <summary>
+ /// Disposes of an EventSource.
+ /// </summary>
+ /// <remarks>
+ /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
+ /// Guidelines:
+ /// 1. We may be called more than once: do nothing after the first call.
+ /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
+ /// </remarks>
+ /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+#if FEATURE_MANAGED_ETW
+ // Send the manifest one more time to ensure circular buffers have a chance to get to this information
+ // even in scenarios with a high volume of ETW events.
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ SendManifest(m_rawManifest);
+ }
+ catch (Exception)
+ { } // If it fails, simply give up.
+ m_eventSourceEnabled = false;
+ }
+ if (m_provider != null)
+ {
+ m_provider.Dispose();
+ m_provider = null;
+ }
+#endif
+ }
+ m_eventSourceEnabled = false;
+ m_eventSourceDisposed = true;
+ }
+ /// <summary>
+ /// Finalizer for EventSource
+ /// </summary>
+ ~EventSource()
+ {
+ this.Dispose(false);
+ }
+ #endregion
+
+ #region private
+#if FEATURE_ACTIVITYSAMPLING
+ internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
+ {
+ Contract.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
+
+ if (m_eventSourceEnabled)
+ {
+ if (listener == null)
+ {
+ WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
+ }
+ else
+ {
+ List<object> arg = new List<object>();
+ arg.Add(msg);
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = 0;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(arg);
+ listener.OnEventWritten(eventCallbackArgs);
+ }
+ }
+ }
+#endif
+
+ [SecurityCritical]
+ private unsafe void WriteEventRaw(
+ string eventName,
+ ref EventDescriptor eventDescriptor,
+ Guid* activityID,
+ Guid* relatedActivityID,
+ int dataCount,
+ IntPtr data)
+ {
+#if FEATURE_MANAGED_ETW
+ if (m_provider == null)
+ {
+ ThrowEventSourceException(eventName);
+ }
+ else
+ {
+ if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
+ ThrowEventSourceException(eventName);
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
+ // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
+ internal EventSource(Guid eventSourceGuid, string eventSourceName)
+ : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
+ { }
+
+ // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
+ internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
+ {
+ m_config = ValidateSettings(settings);
+ Initialize(eventSourceGuid, eventSourceName, traits);
+ }
+
+ /// <summary>
+ /// This method is responsible for the common initialization path from our constructors. It must
+ /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
+ /// "Log", such an exception would become a cached exception for the initialization of the static
+ /// member, and any future access to the "Log" would throw the cached exception).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
+ [SecuritySafeCritical]
+ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
+ {
+ try
+ {
+ m_traits = traits;
+ if (m_traits != null && m_traits.Length % 2 != 0)
+ {
+ throw new ArgumentException(Resources.GetResourceString("TraitEven"), nameof(traits));
+ }
+
+ if (eventSourceGuid == Guid.Empty)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NeedGuid"));
+ }
+
+ if (eventSourceName == null)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NeedName"));
+ }
+
+ m_name = eventSourceName;
+ m_guid = eventSourceGuid;
+#if FEATURE_ACTIVITYSAMPLING
+ m_curLiveSessions = new SessionMask(0);
+ m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ //Enable Implicit Activity tracker
+ m_activityTracker = ActivityTracker.Instance;
+
+#if FEATURE_MANAGED_ETW
+ // Create and register our provider traits. We do this early because it is needed to log errors
+ // In the self-describing event case.
+ this.InitializeProviderMetadata();
+
+ // Register the provider with ETW
+ var provider = new OverideEventProvider(this);
+ provider.Register(eventSourceGuid);
+#endif
+ // Add the eventSource to the global (weak) list.
+ // This also sets m_id, which is the index in the list.
+ EventListener.AddEventSource(this);
+
+#if FEATURE_MANAGED_ETW
+ // OK if we get this far without an exception, then we can at least write out error messages.
+ // Set m_provider, which allows this.
+ m_provider = provider;
+
+#if (!ES_BUILD_STANDALONE && !PROJECTN)
+ // API available on OS >= Win 8 and patched Win 7.
+ // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
+ var osVer = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor;
+ if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || osVer >= 62)
+#endif
+ {
+ int setInformationResult;
+ System.Runtime.InteropServices.GCHandle metadataHandle =
+ System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
+ IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
+
+ setInformationResult = m_provider.SetInformation(
+ UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
+ providerMetadata,
+ (uint)this.providerMetadata.Length);
+
+ metadataHandle.Free();
+ }
+#endif // FEATURE_MANAGED_ETW
+
+ Contract.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
+ // We are logically completely initialized at this point.
+ m_completelyInited = true;
+ }
+ catch (Exception e)
+ {
+ if (m_constructionException == null)
+ m_constructionException = e;
+ ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
+ }
+
+ // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
+ lock (EventListener.EventListenersLock)
+ {
+ // If there are any deferred commands, we can do them now.
+ // This is the most likely place for exceptions to happen.
+ // Note that we are NOT resetting m_deferredCommands to NULL here,
+ // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
+ EventCommandEventArgs deferredCommands = m_deferredCommands;
+ while (deferredCommands != null)
+ {
+ DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
+ deferredCommands = deferredCommands.nextCommand;
+ }
+ }
+ }
+
+ private static string GetName(Type eventSourceType, EventManifestOptions flags)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
+ if (attrib != null && attrib.Name != null)
+ return attrib.Name;
+
+ return eventSourceType.Name;
+ }
+
+ /// <summary>
+ /// Implements the SHA1 hashing algorithm. Note that this
+ /// implementation is for hashing public information. Do not
+ /// use this code to hash private data, as this implementation does
+ /// not take any steps to avoid information disclosure.
+ /// </summary>
+ private struct Sha1ForNonSecretPurposes
+ {
+ private long length; // Total message length in bits
+ private uint[] w; // Workspace
+ private int pos; // Length of current chunk in bytes
+
+ /// <summary>
+ /// Call Start() to initialize the hash object.
+ /// </summary>
+ public void Start()
+ {
+ if (this.w == null)
+ {
+ this.w = new uint[85];
+ }
+
+ this.length = 0;
+ this.pos = 0;
+ this.w[80] = 0x67452301;
+ this.w[81] = 0xEFCDAB89;
+ this.w[82] = 0x98BADCFE;
+ this.w[83] = 0x10325476;
+ this.w[84] = 0xC3D2E1F0;
+ }
+
+ /// <summary>
+ /// Adds an input byte to the hash.
+ /// </summary>
+ /// <param name="input">Data to include in the hash.</param>
+ public void Append(byte input)
+ {
+ this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
+ if (64 == ++this.pos)
+ {
+ this.Drain();
+ }
+ }
+
+ /// <summary>
+ /// Adds input bytes to the hash.
+ /// </summary>
+ /// <param name="input">
+ /// Data to include in the hash. Must not be null.
+ /// </param>
+ public void Append(byte[] input)
+ {
+ foreach (var b in input)
+ {
+ this.Append(b);
+ }
+ }
+
+ /// <summary>
+ /// Retrieves the hash value.
+ /// Note that after calling this function, the hash object should
+ /// be considered uninitialized. Subsequent calls to Append or
+ /// Finish will produce useless results. Call Start() to
+ /// reinitialize.
+ /// </summary>
+ /// <param name="output">
+ /// Buffer to receive the hash value. Must not be null.
+ /// Up to 20 bytes of hash will be written to the output buffer.
+ /// If the buffer is smaller than 20 bytes, the remaining hash
+ /// bytes will be lost. If the buffer is larger than 20 bytes, the
+ /// rest of the buffer is left unmodified.
+ /// </param>
+ public void Finish(byte[] output)
+ {
+ long l = this.length + 8 * this.pos;
+ this.Append(0x80);
+ while (this.pos != 56)
+ {
+ this.Append(0x00);
+ }
+
+ unchecked
+ {
+ this.Append((byte)(l >> 56));
+ this.Append((byte)(l >> 48));
+ this.Append((byte)(l >> 40));
+ this.Append((byte)(l >> 32));
+ this.Append((byte)(l >> 24));
+ this.Append((byte)(l >> 16));
+ this.Append((byte)(l >> 8));
+ this.Append((byte)l);
+
+ int end = output.Length < 20 ? output.Length : 20;
+ for (int i = 0; i != end; i++)
+ {
+ uint temp = this.w[80 + i / 4];
+ output[i] = (byte)(temp >> 24);
+ this.w[80 + i / 4] = temp << 8;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Called when this.pos reaches 64.
+ /// </summary>
+ private void Drain()
+ {
+ for (int i = 16; i != 80; i++)
+ {
+ this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
+ }
+
+ unchecked
+ {
+ uint a = this.w[80];
+ uint b = this.w[81];
+ uint c = this.w[82];
+ uint d = this.w[83];
+ uint e = this.w[84];
+
+ for (int i = 0; i != 20; i++)
+ {
+ const uint k = 0x5A827999;
+ uint f = (b & c) | ((~b) & d);
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 20; i != 40; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0x6ED9EBA1;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 40; i != 60; i++)
+ {
+ uint f = (b & c) | (b & d) | (c & d);
+ const uint k = 0x8F1BBCDC;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 60; i != 80; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0xCA62C1D6;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ this.w[80] += a;
+ this.w[81] += b;
+ this.w[82] += c;
+ this.w[83] += d;
+ this.w[84] += e;
+ }
+
+ this.length += 512; // 64 bytes == 512 bits
+ this.pos = 0;
+ }
+
+ private static uint Rol1(uint input)
+ {
+ return (input << 1) | (input >> 31);
+ }
+
+ private static uint Rol5(uint input)
+ {
+ return (input << 5) | (input >> 27);
+ }
+
+ private static uint Rol30(uint input)
+ {
+ return (input << 30) | (input >> 2);
+ }
+ }
+
+ private static Guid GenerateGuidFromName(string name)
+ {
+ byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
+ var hash = new Sha1ForNonSecretPurposes();
+ hash.Start();
+ hash.Append(namespaceBytes);
+ hash.Append(bytes);
+ Array.Resize(ref bytes, 16);
+ hash.Finish(bytes);
+
+ bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
+ return new Guid(bytes);
+ }
+
+ [SecurityCritical]
+ private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
+ {
+ // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
+ // the recursion, but can we do this in a robust way?
+
+ IntPtr dataPointer = data->DataPointer;
+ // advance to next EventData in array
+ ++data;
+
+ Type dataType = GetDataType(m_eventData[eventId], parameterId);
+
+ Again:
+ if (dataType == typeof(IntPtr))
+ {
+ return *((IntPtr*)dataPointer);
+ }
+ else if (dataType == typeof(int))
+ {
+ return *((int*)dataPointer);
+ }
+ else if (dataType == typeof(uint))
+ {
+ return *((uint*)dataPointer);
+ }
+ else if (dataType == typeof(long))
+ {
+ return *((long*)dataPointer);
+ }
+ else if (dataType == typeof(ulong))
+ {
+ return *((ulong*)dataPointer);
+ }
+ else if (dataType == typeof(byte))
+ {
+ return *((byte*)dataPointer);
+ }
+ else if (dataType == typeof(sbyte))
+ {
+ return *((sbyte*)dataPointer);
+ }
+ else if (dataType == typeof(short))
+ {
+ return *((short*)dataPointer);
+ }
+ else if (dataType == typeof(ushort))
+ {
+ return *((ushort*)dataPointer);
+ }
+ else if (dataType == typeof(float))
+ {
+ return *((float*)dataPointer);
+ }
+ else if (dataType == typeof(double))
+ {
+ return *((double*)dataPointer);
+ }
+ else if (dataType == typeof(decimal))
+ {
+ return *((decimal*)dataPointer);
+ }
+ else if (dataType == typeof(bool))
+ {
+ // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
+ if (*((int*)dataPointer) == 1)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (dataType == typeof(Guid))
+ {
+ return *((Guid*)dataPointer);
+ }
+ else if (dataType == typeof(char))
+ {
+ return *((char*)dataPointer);
+ }
+ else if (dataType == typeof(DateTime))
+ {
+ long dateTimeTicks = *((long*)dataPointer);
+ return DateTime.FromFileTimeUtc(dateTimeTicks);
+ }
+ else if (dataType == typeof(byte[]))
+ {
+ // byte[] are written to EventData* as an int followed by a blob
+ int cbSize = *((int*)dataPointer);
+ byte[] blob = new byte[cbSize];
+ dataPointer = data->DataPointer;
+ data++;
+ for (int i = 0; i < cbSize; ++i)
+ blob[i] = *((byte*)(dataPointer + i));
+ return blob;
+ }
+ else if (dataType == typeof(byte*))
+ {
+ // TODO: how do we want to handle this? For now we ignore it...
+ return null;
+ }
+ else
+ {
+ if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
+ {
+ return null;
+ }
+
+ try
+ {
+ m_EventSourceInDecodeObject = true;
+
+ if (dataType.IsEnum())
+ {
+ dataType = Enum.GetUnderlyingType(dataType);
+ goto Again;
+ }
+
+
+ // Everything else is marshaled as a string.
+ // ETW strings are NULL-terminated, so marshal everything up to the first
+ // null in the string.
+ return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
+
+ }
+ finally
+ {
+ m_EventSourceInDecodeObject = false;
+ }
+ }
+ }
+
+ // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
+ // eventSource).
+ private EventDispatcher GetDispatcher(EventListener listener)
+ {
+ EventDispatcher dispatcher = m_Dispatchers;
+ while (dispatcher != null)
+ {
+ if (dispatcher.m_Listener == listener)
+ return dispatcher;
+ dispatcher = dispatcher.m_Next;
+ }
+ return dispatcher;
+ }
+
+ [SecurityCritical]
+ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
+ {
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
+ if (childActivityID != null)
+ {
+ ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
+
+ // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
+ // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
+ // During manifest creation we modify the ParameterInfo[] that we store to strip out any
+ // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
+ // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
+ // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
+ // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
+ // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
+ if (!m_eventData[eventId].HasRelatedActivityID)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NoRelatedActivityId"));
+ }
+ }
+
+ LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
+#if FEATURE_MANAGED_ETW
+ if (m_eventData[eventId].EnabledForETW)
+ {
+ Guid* pActivityId = null;
+ Guid activityId = Guid.Empty;
+ Guid relatedActivityId = Guid.Empty;
+ EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
+ EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
+
+ if (childActivityID == null &&
+ ((activityOptions & EventActivityOptions.Disable) == 0))
+ {
+ if (opcode == EventOpcode.Start)
+ {
+ m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
+ }
+ else if (opcode == EventOpcode.Stop)
+ {
+ m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
+ }
+
+ if (activityId != Guid.Empty)
+ pActivityId = &activityId;
+ if (relatedActivityId != Guid.Empty)
+ childActivityID = &relatedActivityId;
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
+ SessionMask etwSessions = SessionMask.All;
+ // only compute etwSessions if there are *any* ETW filters enabled...
+ if ((ulong)m_curLiveSessions != 0)
+ etwSessions = GetEtwSessionMask(eventId, childActivityID);
+
+ if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
+ {
+ if (!SelfDescribingEvents)
+ {
+ if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
+ {
+ // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
+ // mask set to 0x0f so, when all ETW sessions want the event we don't need to
+ // synthesize a new one
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // only some of the ETW sessions will receive this event. Synthesize a new
+ // Descriptor whose Keywords field will have the appropriate bits set.
+ var desc = new EventDescriptor(
+ m_eventData[eventId].Descriptor.EventId,
+ m_eventData[eventId].Descriptor.Version,
+ m_eventData[eventId].Descriptor.Channel,
+ m_eventData[eventId].Descriptor.Level,
+ m_eventData[eventId].Descriptor.Opcode,
+ m_eventData[eventId].Descriptor.Task,
+ unchecked((long)etwSessions.ToEventKeywords() | origKwd));
+
+ if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
+ }
+ }
+#else
+ if (!SelfDescribingEvents)
+ {
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+#endif // FEATURE_MANAGED_ETW
+ if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
+ {
+#if (!ES_BUILD_STANDALONE && !PROJECTN)
+ // Maintain old behavior - object identity is preserved
+ if (AppContextSwitches.PreserveEventListnerObjectIdentity)
+ {
+ WriteToAllListeners(eventId, childActivityID, args);
+ }
+ else
+#endif // !ES_BUILD_STANDALONE
+ {
+ object[] serializedArgs = SerializeEventArgs(eventId, args);
+ WriteToAllListeners(eventId, childActivityID, serializedArgs);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(m_eventData[eventId].Name, ex);
+ }
+ }
+ }
+
+ [SecurityCritical]
+ unsafe private object[] SerializeEventArgs(int eventId, object[] args)
+ {
+ TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
+ if (eventTypes == null)
+ {
+ eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
+ }
+ var eventData = new object[eventTypes.typeInfos.Length];
+ for (int i = 0; i < eventTypes.typeInfos.Length; i++)
+ {
+ eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
+ }
+ return eventData;
+ }
+
+ /// <summary>
+ /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
+ /// checks that they in fact match and logs a warning to the debugger if they don't.
+ /// </summary>
+ /// <param name="infos"></param>
+ /// <param name="args"></param>
+ private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
+ {
+#if (!ES_BUILD_PCL && !PROJECTN)
+ // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
+ // writing to the debugger log on PCL.
+ bool typesMatch = args.Length == infos.Length;
+
+ int i = 0;
+ while (typesMatch && i < args.Length)
+ {
+ Type pType = infos[i].ParameterType;
+
+ // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
+ // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
+ // argument is null and the parameter type is non-nullable.
+ if ((args[i] != null && (args[i].GetType() != pType))
+ || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
+ )
+ {
+ typesMatch = false;
+ break;
+ }
+
+ ++i;
+ }
+
+ if (!typesMatch)
+ {
+ System.Diagnostics.Debugger.Log(0, null, Resources.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
+ }
+#endif //!ES_BUILD_PCL
+ }
+
+ private int GetParamLenghtIncludingByteArray(ParameterInfo[] parameters)
+ {
+ int sum = 0;
+ foreach(ParameterInfo info in parameters)
+ {
+ if(info.ParameterType == typeof(byte[]))
+ {
+ sum += 2;
+ }
+ else
+ {
+ sum++;
+ }
+ }
+
+ return sum;
+ }
+
+ [SecurityCritical]
+ unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
+ {
+ // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
+ // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
+ // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
+ // we just used the modifiedParamCount here -- so we need both.
+ int paramCount = m_eventData[eventId].Parameters.Length;
+ int modifiedParamCount = GetParamLenghtIncludingByteArray(m_eventData[eventId].Parameters);
+ if (eventDataCount != modifiedParamCount)
+ {
+ ReportOutOfBandMessage(Resources.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
+ paramCount = Math.Min(paramCount, eventDataCount);
+ }
+
+ object[] args = new object[paramCount];
+
+ EventSource.EventData* dataPtr = data;
+ for (int i = 0; i < paramCount; i++)
+ args[i] = DecodeObject(eventId, i, ref dataPtr);
+ WriteToAllListeners(eventId, childActivityID, args);
+ }
+
+ // helper for writing to all EventListeners attached the current eventSource.
+ [SecurityCritical]
+ unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = eventId;
+ if (childActivityID != null)
+ eventCallbackArgs.RelatedActivityId = *childActivityID;
+ eventCallbackArgs.EventName = m_eventData[eventId].Name;
+ eventCallbackArgs.Message = m_eventData[eventId].Message;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
+
+ DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
+ }
+
+ [SecurityCritical]
+ private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
+ {
+ Exception lastThrownException = null;
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ Contract.Assert(dispatcher.m_EventEnabled != null);
+ if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
+ {
+#if FEATURE_ACTIVITYSAMPLING
+ var activityFilter = dispatcher.m_Listener.m_activityFilter;
+ // order below is important as PassesActivityFilter will "flow" active activities
+ // even when the current EventSource doesn't have filtering enabled. This allows
+ // interesting activities to be updated so that sources that do sample can get
+ // accurate data
+ if (activityFilter == null ||
+ ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
+ m_eventData[eventId].TriggersActivityTracking > 0,
+ this, eventId) ||
+ !dispatcher.m_activityFilteringEnabled)
+#endif // FEATURE_ACTIVITYSAMPLING
+ {
+ try
+ {
+ dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
+ }
+ catch (Exception e)
+ {
+ ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
+ + e.Message, false);
+ lastThrownException = e;
+ }
+ }
+ }
+ }
+
+ if (lastThrownException != null)
+ {
+ throw new EventSourceException(lastThrownException);
+ }
+ }
+
+ [SecuritySafeCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
+ {
+#if FEATURE_MANAGED_ETW
+ if (m_provider != null)
+ {
+ string eventName = "EventSourceMessage";
+ if (SelfDescribingEvents)
+ {
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked(keywords),
+ Level = level
+ };
+ var msg = new { message = msgString };
+ var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
+ WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
+ }
+ else
+ {
+ // We want the name of the provider to show up so if we don't have a manifest we create
+ // on that at least has the provider name (I don't define any events).
+ if (m_rawManifest == null && m_outOfBandMessageCount == 1)
+ {
+ ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
+ manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
+ manifestBuilder.AddEventParameter(typeof(string), "message");
+ manifestBuilder.EndEvent();
+ SendManifest(manifestBuilder.CreateManifest());
+ }
+
+ // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
+ fixed (char* msgStringPtr = msgString)
+ {
+ EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
+ EventProvider.EventData data = new EventProvider.EventData();
+ data.Ptr = (ulong)msgStringPtr;
+ data.Size = (uint)(2 * (msgString.Length + 1));
+ data.Reserved = 0;
+ m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
+ }
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ /// <summary>
+ /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
+ /// while writing the message to any one of the listeners will be silently ignored.
+ /// </summary>
+ private void WriteStringToAllListeners(string eventName, string msg)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = 0;
+ eventCallbackArgs.Message = msg;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
+ eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
+ eventCallbackArgs.EventName = eventName;
+
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ bool dispatcherEnabled = false;
+ if (dispatcher.m_EventEnabled == null)
+ {
+ // if the listeners that weren't correctly initialized, we will send to it
+ // since this is an error message and we want to see it go out.
+ dispatcherEnabled = true;
+ }
+ else
+ {
+ // if there's *any* enabled event on the dispatcher we'll write out the string
+ // otherwise we'll treat the listener as disabled and skip it
+ for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
+ {
+ if (dispatcher.m_EventEnabled[evtId])
+ {
+ dispatcherEnabled = true;
+ break;
+ }
+ }
+ }
+ try
+ {
+ if (dispatcherEnabled)
+ dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
+ }
+ catch
+ {
+ // ignore any exceptions thrown by listeners' OnEventWritten
+ }
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ [SecurityCritical]
+ unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
+ {
+ SessionMask etwSessions = new SessionMask();
+
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[i];
+ if (etwSession != null)
+ {
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ // PassesActivityFilter() will flow "interesting" activities, so make sure
+ // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
+ // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
+ // do not fire events indiscriminately, when no filters are specified, but only
+ // if, in addition, the session did not also enable ActivitySampling)
+ if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
+ activityFilter != null &&
+ ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
+ m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
+ !m_activityFilteringForETWEnabled[i])
+ {
+ etwSessions[i] = true;
+ }
+ }
+ }
+ // flow "interesting" activities for all legacy sessions in which there's some
+ // level of activity tracing enabled (even other EventSources)
+ if (m_legacySessions != null && m_legacySessions.Count > 0 &&
+ (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
+ {
+ // only calculate InternalCurrentThreadActivityId once
+ Guid* pCurrentActivityId = null;
+ Guid currentActivityId;
+ foreach (var legacyEtwSession in m_legacySessions)
+ {
+ if (legacyEtwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
+ if (activityFilter != null)
+ {
+ if (pCurrentActivityId == null)
+ {
+ currentActivityId = InternalCurrentThreadActivityId;
+ pCurrentActivityId = &currentActivityId;
+ }
+ ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
+ }
+ }
+ }
+
+ return etwSessions;
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
+ /// It is possible that eventSources turn off the event based on additional filtering criteria.
+ /// </summary>
+ private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
+ {
+ if (!enable)
+ return false;
+
+ EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
+ EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
+#else
+ EventChannel channel = EventChannel.None;
+#endif
+
+ return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
+ }
+
+ private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
+ EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
+ {
+ if (!enabled)
+ return false;
+
+ // does is pass the level test?
+ if ((currentLevel != 0) && (currentLevel < eventLevel))
+ return false;
+
+ // if yes, does it pass the keywords test?
+ if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
+ {
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // is there a channel with keywords that match currentMatchAnyKeyword?
+ if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
+ {
+ EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
+ if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
+ return false;
+ }
+ else
+#endif
+ {
+ if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
+ return false;
+ }
+ }
+ return true;
+
+ }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ private void ThrowEventSourceException(string eventName, Exception innerEx = null)
+ {
+ // If we fail during out of band logging we may end up trying
+ // to throw another EventSourceException, thus hitting a StackOverflowException.
+ // Avoid StackOverflow by making sure we do not recursively call this method.
+ if (m_EventSourceExceptionRecurenceCount > 0)
+ return;
+ try
+ {
+ m_EventSourceExceptionRecurenceCount++;
+
+ string errorPrefix = "EventSourceException";
+ if(eventName != null)
+ {
+ errorPrefix += " while processing event \"" + eventName + "\"";
+ }
+
+ // TODO Create variations of EventSourceException that indicate more information using the error code.
+ switch (EventProvider.GetLastWriteEventError())
+ {
+ case EventProvider.WriteEventErrorCode.EventTooBig:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_EventTooBig"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_EventTooBig"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.NoFreeBuffers:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NoFreeBuffers"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.NullInput:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NullInput"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NullInput"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.TooManyArgs:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_TooManyArgs"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_TooManyArgs"), innerEx);
+ break;
+ default:
+ if (innerEx != null)
+ ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
+ else
+ ReportOutOfBandMessage(errorPrefix, true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
+ break;
+ }
+ }
+ finally
+ {
+ m_EventSourceExceptionRecurenceCount--;
+ }
+ }
+
+ private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
+ {
+ if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
+ (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
+ (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
+ {
+ ThrowEventSourceException(eventName);
+ }
+ }
+
+ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
+ {
+ if (opcode == EventOpcode.Info && eventName != null)
+ {
+ if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
+ {
+ return EventOpcode.Start;
+ }
+ else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
+ {
+ return EventOpcode.Stop;
+ }
+ }
+
+ return opcode;
+ }
+
+#if FEATURE_MANAGED_ETW
+ /// <summary>
+ /// This class lets us hook the 'OnEventCommand' from the eventSource.
+ /// </summary>
+ private class OverideEventProvider : EventProvider
+ {
+ public OverideEventProvider(EventSource eventSource)
+ {
+ this.m_eventSource = eventSource;
+ }
+ protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
+ int perEventSourceSessionId, int etwSessionId)
+ {
+ // We use null to represent the ETW EventListener.
+ EventListener listener = null;
+ m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
+ (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
+ }
+ private EventSource m_eventSource;
+ }
+#endif
+
+ /// <summary>
+ /// Used to hold all the static information about an event. This includes everything in the event
+ /// descriptor as well as some stuff we added specifically for EventSource. see the
+ /// code:m_eventData for where we use this.
+ /// </summary>
+ internal partial struct EventMetadata
+ {
+ public EventDescriptor Descriptor;
+ public EventTags Tags;
+ public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
+ public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
+
+ public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
+#if !FEATURE_ACTIVITYSAMPLING
+#pragma warning disable 0649
+#endif
+ public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
+#if !FEATURE_ACTIVITYSAMPLING
+#pragma warning restore 0649
+#endif
+ public string Name; // the name of the event
+ public string Message; // If the event has a message associated with it, this is it.
+ public ParameterInfo[] Parameters; // TODO can we remove?
+
+ public TraceLoggingEventTypes TraceLoggingEventTypes;
+ public EventActivityOptions ActivityOptions;
+
+#if PROJECTN
+ public EventParameterType[] ParameterTypes;
+#endif
+ };
+
+ // This is the internal entry point that code:EventListeners call when wanting to send a command to a
+ // eventSource. The logic is as follows
+ //
+ // * if Command == Update
+ // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
+ // to (if listener != null)
+ // perEventSourceSessionId = 0 - reserved for EventListeners
+ // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
+ // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
+ // Keywords that identifies the session
+ // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
+ // discriminated by etwSessionId
+ // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
+ // activity tracing across different providers (which might have different sessionIds
+ // for the same ETW session)
+ // * enable, level, matchAnyKeywords are used to set a default for all events for the
+ // eventSource. In particular, if 'enabled' is false, 'level' and
+ // 'matchAnyKeywords' are not used.
+ // * OnEventCommand is invoked, which may cause calls to
+ // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
+ // depending on the logic in that routine.
+ // * else (command != Update)
+ // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
+ // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
+ //
+ // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
+ internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
+ EventCommand command, bool enable,
+ EventLevel level, EventKeywords matchAnyKeyword,
+ IDictionary<string, string> commandArguments)
+ {
+ var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
+ lock (EventListener.EventListenersLock)
+ {
+ if (m_completelyInited)
+ {
+ // After the first command arrive after construction, we are ready to get rid of the deferred commands
+ this.m_deferredCommands = null;
+ // We are fully initialized, do the command
+ DoCommand(commandArgs);
+ }
+ else
+ {
+ // We can't do the command, simply remember it and we do it when we are fully constructed.
+ commandArgs.nextCommand = m_deferredCommands;
+ m_deferredCommands = commandArgs;
+ }
+ }
+ }
+
+ /// <summary>
+ /// We want the eventSource to be fully initialized when we do commands because that way we can send
+ /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
+ /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
+ /// This helper actually does all actual command logic.
+ /// </summary>
+ internal void DoCommand(EventCommandEventArgs commandArgs)
+ {
+ // PRECONDITION: We should be holding the EventListener.EventListenersLock
+ // We defer commands until we are completely inited. This allows error messages to be sent.
+ Contract.Assert(m_completelyInited);
+
+#if FEATURE_MANAGED_ETW
+ if (m_provider == null) // If we failed to construct
+ return;
+#endif // FEATURE_MANAGED_ETW
+
+ m_outOfBandMessageCount = 0;
+ bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
+ try
+ {
+ EnsureDescriptorsInitialized();
+ Contract.Assert(m_eventData != null);
+
+ // Find the per-EventSource dispatcher corresponding to registered dispatcher
+ commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
+ if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_ListenerNotFound"));
+ }
+
+ if (commandArgs.Arguments == null)
+ commandArgs.Arguments = new Dictionary<string, string>();
+
+ if (commandArgs.Command == EventCommand.Update)
+ {
+ // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
+ for (int i = 0; i < m_eventData.Length; i++)
+ EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
+
+ if (commandArgs.enable)
+ {
+ if (!m_eventSourceEnabled)
+ {
+ // EventSource turned on for the first time, simply copy the bits.
+ m_level = commandArgs.level;
+ m_matchAnyKeyword = commandArgs.matchAnyKeyword;
+ }
+ else
+ {
+ // Already enabled, make it the most verbose of the existing and new filter
+ if (commandArgs.level > m_level)
+ m_level = commandArgs.level;
+ if (commandArgs.matchAnyKeyword == 0)
+ m_matchAnyKeyword = 0;
+ else if (m_matchAnyKeyword != 0)
+ m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
+ }
+ }
+
+ // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
+ // represent 0-based positive values
+ bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
+ if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
+ bSessionEnable = false;
+
+ if (commandArgs.listener == null)
+ {
+ if (!bSessionEnable)
+ commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
+ // for "global" enable/disable (passed in with listener == null and
+ // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
+ --commandArgs.perEventSourceSessionId;
+ }
+
+ commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
+
+ // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
+ // hasn't changed.
+ // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
+ // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
+ Contract.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
+
+ // Send the manifest if we are enabling an ETW session
+ if (bSessionEnable && commandArgs.dispatcher == null)
+ {
+ // eventSourceDispatcher == null means this is the ETW manifest
+
+ // Note that we unconditionally send the manifest whenever we are enabled, even if
+ // we were already enabled. This is because there may be multiple sessions active
+ // and we can't know that all the sessions have seen the manifest.
+ if (!SelfDescribingEvents)
+ SendManifest(m_rawManifest);
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
+ {
+ bool participateInSampling = false;
+ string activityFilters;
+ int sessionIdBit;
+
+ ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
+ out activityFilters, out sessionIdBit);
+
+ if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_SessionIdError",
+ commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
+ sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
+ }
+
+ if (commandArgs.listener == null)
+ {
+ UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
+ }
+ else
+ {
+ ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
+ commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
+ }
+ }
+ else if (!bSessionEnable && commandArgs.listener == null)
+ {
+ // if we disable an ETW session, indicate that in a synthesized command argument
+ if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
+ {
+ commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
+ }
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
+ // things like log messages, or test if keywords are enabled in the callback.
+ if (commandArgs.enable)
+ {
+ Contract.Assert(m_eventData != null);
+ m_eventSourceEnabled = true;
+ }
+
+ this.OnEventCommand(commandArgs);
+ var eventCommandCallback = this.m_eventCommandExecuted;
+ if (eventCommandCallback != null)
+ eventCommandCallback(this, commandArgs);
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
+ {
+ // if we disable an ETW session, complete disabling it
+ UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ if (!commandArgs.enable)
+ {
+ // If we are disabling, maybe we can turn on 'quick checks' to filter
+ // quickly. These are all just optimizations (since later checks will still filter)
+
+#if FEATURE_ACTIVITYSAMPLING
+ // Turn off (and forget) any information about Activity Tracing.
+ if (commandArgs.listener == null)
+ {
+ // reset all filtering information for activity-tracing-aware sessions
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[i];
+ if (etwSession != null)
+ ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
+ }
+ m_activityFilteringForETWEnabled = new SessionMask(0);
+ m_curLiveSessions = new SessionMask(0);
+ // reset activity-tracing-aware sessions
+ if (m_etwSessionIdMap != null)
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ m_etwSessionIdMap[i] = null;
+ // reset legacy sessions
+ if (m_legacySessions != null)
+ m_legacySessions.Clear();
+ }
+ else
+ {
+ ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
+ commandArgs.dispatcher.m_activityFilteringEnabled = false;
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // There is a good chance EnabledForAnyListener are not as accurate as
+ // they could be, go ahead and get a better estimate.
+ for (int i = 0; i < m_eventData.Length; i++)
+ {
+ bool isEnabledForAnyListener = false;
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ if (dispatcher.m_EventEnabled[i])
+ {
+ isEnabledForAnyListener = true;
+ break;
+ }
+ }
+ m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
+ }
+
+ // If no events are enabled, disable the global enabled bit.
+ if (!AnyEventEnabled())
+ {
+ m_level = 0;
+ m_matchAnyKeyword = 0;
+ m_eventSourceEnabled = false;
+ }
+ }
+#if FEATURE_ACTIVITYSAMPLING
+ UpdateKwdTriggers(commandArgs.enable);
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+ else
+ {
+ if (commandArgs.Command == EventCommand.SendManifest)
+ {
+ // TODO: should we generate the manifest here if we hadn't already?
+ if (m_rawManifest != null)
+ SendManifest(m_rawManifest);
+ }
+
+ // These are not used for non-update commands and thus should always be 'default' values
+ // Contract.Assert(enable == true);
+ // Contract.Assert(level == EventLevel.LogAlways);
+ // Contract.Assert(matchAnyKeyword == EventKeywords.None);
+
+ this.OnEventCommand(commandArgs);
+ var eventCommandCallback = m_eventCommandExecuted;
+ if (eventCommandCallback != null)
+ eventCommandCallback(this, commandArgs);
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (m_completelyInited && (commandArgs.listener != null || shouldReport))
+ {
+ SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
+ ReportActivitySamplingInfo(commandArgs.listener, m);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+ catch (Exception e)
+ {
+ // When the ETW session is created after the EventSource has registered with the ETW system
+ // we can send any error messages here.
+ ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
+ // We never throw when doing a command.
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+
+ internal void UpdateEtwSession(
+ int sessionIdBit,
+ int etwSessionId,
+ bool bEnable,
+ string activityFilters,
+ bool participateInSampling)
+ {
+ if (sessionIdBit < SessionMask.MAX)
+ {
+ // activity-tracing-aware etw session
+ if (bEnable)
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
+ ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
+ m_etwSessionIdMap[sessionIdBit] = etwSession;
+ m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
+ }
+ else
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId);
+ m_etwSessionIdMap[sessionIdBit] = null;
+ m_activityFilteringForETWEnabled[sessionIdBit] = false;
+ if (etwSession != null)
+ {
+ ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
+ // the ETW session is going away; remove it from the global list
+ EtwSession.RemoveEtwSession(etwSession);
+ }
+ }
+ m_curLiveSessions[sessionIdBit] = bEnable;
+ }
+ else
+ {
+ // legacy etw session
+ if (bEnable)
+ {
+ if (m_legacySessions == null)
+ m_legacySessions = new List<EtwSession>(8);
+ var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
+ if (!m_legacySessions.Contains(etwSession))
+ m_legacySessions.Add(etwSession);
+ }
+ else
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId);
+ if (etwSession != null)
+ {
+ if (m_legacySessions != null)
+ m_legacySessions.Remove(etwSession);
+ // the ETW session is going away; remove it from the global list
+ EtwSession.RemoveEtwSession(etwSession);
+ }
+ }
+ }
+ }
+
+ internal static bool ParseCommandArgs(
+ IDictionary<string, string> commandArguments,
+ out bool participateInSampling,
+ out string activityFilters,
+ out int sessionIdBit)
+ {
+ bool res = true;
+ participateInSampling = false;
+ string activityFilterString;
+ if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
+ {
+ // if a start event is specified default the event source to participate in sampling
+ participateInSampling = true;
+ }
+
+ if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
+ {
+ if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
+ activityFilterString == "0")
+ participateInSampling = false;
+ else
+ participateInSampling = true;
+ }
+
+ string sSessionKwd;
+ int sessionKwd = -1;
+ if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
+ !int.TryParse(sSessionKwd, out sessionKwd) ||
+ sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
+ sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
+ {
+ sessionIdBit = -1;
+ res = false;
+ }
+ else
+ {
+ sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
+ }
+ return res;
+ }
+
+ internal void UpdateKwdTriggers(bool enable)
+ {
+ if (enable)
+ {
+ // recompute m_keywordTriggers
+ ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
+ if (gKeywords == 0)
+ gKeywords = 0xFFFFffffFFFFffff;
+
+ m_keywordTriggers = 0;
+ for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[sessId];
+ if (etwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
+ }
+ }
+ else
+ {
+ m_keywordTriggers = 0;
+ }
+ }
+
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
+ /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
+ /// range return false, otherwise true.
+ /// </summary>
+ internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
+ {
+ if (dispatcher == null)
+ {
+ if (eventId >= m_eventData.Length)
+ return false;
+#if FEATURE_MANAGED_ETW
+ if (m_provider != null)
+ m_eventData[eventId].EnabledForETW = value;
+#endif
+ }
+ else
+ {
+ if (eventId >= dispatcher.m_EventEnabled.Length)
+ return false;
+ dispatcher.m_EventEnabled[eventId] = value;
+ if (value)
+ m_eventData[eventId].EnabledForAnyListener = true;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true if any event at all is on.
+ /// </summary>
+ private bool AnyEventEnabled()
+ {
+ for (int i = 0; i < m_eventData.Length; i++)
+ if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
+ return true;
+ return false;
+ }
+
+ private bool IsDisposed
+ {
+ get { return m_eventSourceDisposed; }
+ }
+
+ [SecuritySafeCritical]
+ private void EnsureDescriptorsInitialized()
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ if (m_eventData == null)
+ {
+ Guid eventSourceGuid = Guid.Empty;
+ string eventSourceName = null;
+ EventMetadata[] eventData = null;
+ byte[] manifest = null;
+
+ // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
+ // to the reflection approach.
+ GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
+
+ if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
+ {
+ // GetMetadata failed, so we have to set it via reflection.
+ Contract.Assert(m_rawManifest == null);
+ m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
+ Contract.Assert(m_eventData != null);
+
+ }
+ else
+ {
+ // GetMetadata worked, so set the fields as appropriate.
+ m_name = eventSourceName;
+ m_guid = eventSourceGuid;
+ m_eventData = eventData;
+ m_rawManifest = manifest;
+ }
+ // TODO Enforce singleton pattern
+ foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
+ {
+ if (eventSource != this)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
+ }
+ }
+ }
+
+ // Make certain all dispatchers also have their arrays initialized
+ EventDispatcher dispatcher = m_Dispatchers;
+ while (dispatcher != null)
+ {
+ if (dispatcher.m_EventEnabled == null)
+ dispatcher.m_EventEnabled = new bool[m_eventData.Length];
+ dispatcher = dispatcher.m_Next;
+ }
+ }
+ if (s_currentPid == 0)
+ {
+#if ES_BUILD_STANDALONE && !ES_BUILD_PCL
+ // for non-BCL EventSource we must assert SecurityPermission
+ new SecurityPermission(PermissionState.Unrestricted).Assert();
+#endif
+ s_currentPid = Win32Native.GetCurrentProcessId();
+ }
+ }
+
+ // Send out the ETW manifest XML out to ETW
+ // Today, we only send the manifest to ETW, custom listeners don't get it.
+ [SecuritySafeCritical]
+ private unsafe bool SendManifest(byte[] rawManifest)
+ {
+ bool success = true;
+
+ if (rawManifest == null)
+ return false;
+
+ Contract.Assert(!SelfDescribingEvents);
+
+#if FEATURE_MANAGED_ETW
+ fixed (byte* dataPtr = rawManifest)
+ {
+ // we don't want the manifest to show up in the event log channels so we specify as keywords
+ // everything but the first 8 bits (reserved for the 8 channels)
+ var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
+ ManifestEnvelope envelope = new ManifestEnvelope();
+
+ envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
+ envelope.MajorVersion = 1;
+ envelope.MinorVersion = 0;
+ envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
+ int dataLeft = rawManifest.Length;
+ envelope.ChunkNumber = 0;
+
+ EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
+
+ dataDescrs[0].Ptr = (ulong)&envelope;
+ dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
+ dataDescrs[0].Reserved = 0;
+
+ dataDescrs[1].Ptr = (ulong)dataPtr;
+ dataDescrs[1].Reserved = 0;
+
+ int chunkSize = ManifestEnvelope.MaxChunkSize;
+ TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
+ envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
+ while (dataLeft > 0)
+ {
+ dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
+ if (m_provider != null)
+ {
+ if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
+ {
+ // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
+ // can fail. If we get this failure on the first chunk try again with something smaller
+ // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
+ if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
+ {
+ if (envelope.ChunkNumber == 0 && chunkSize > 256)
+ {
+ chunkSize = chunkSize / 2;
+ goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
+ }
+ }
+ success = false;
+ if (ThrowOnEventWriteErrors)
+ ThrowEventSourceException("SendManifest");
+ break;
+ }
+ }
+ dataLeft -= chunkSize;
+ dataDescrs[1].Ptr += (uint)chunkSize;
+ envelope.ChunkNumber++;
+
+ // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
+ // 5 chunks, so only the largest manifests will hit the pause.
+ if((envelope.ChunkNumber % 5) == 0)
+ Thread.Sleep(15);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ return success;
+ }
+
+#if (ES_BUILD_PCL || PROJECTN)
+ internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
+ {
+ return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
+ }
+#endif
+
+ // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
+ // When that is the case, we have the build the custom assemblies on a member by hand.
+ internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
+ {
+ if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
+ {
+ // Let the runtime to the work for us, since we can execute code in this context.
+ Attribute firstAttribute = null;
+ foreach (var attribute in member.GetCustomAttributes(attributeType, false))
+ {
+ firstAttribute = (Attribute)attribute;
+ break;
+ }
+ return firstAttribute;
+ }
+
+#if (!ES_BUILD_PCL && !PROJECTN)
+ // In the reflection only context, we have to do things by hand.
+ string fullTypeNameToFind = attributeType.FullName;
+
+#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
+ fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
+#endif
+
+ foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
+ {
+ if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
+ {
+ Attribute attr = null;
+
+ Contract.Assert(data.ConstructorArguments.Count <= 1);
+
+ if (data.ConstructorArguments.Count == 1)
+ {
+ attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
+ }
+ else if (data.ConstructorArguments.Count == 0)
+ {
+ attr = (Attribute)Activator.CreateInstance(attributeType);
+ }
+
+ if (attr != null)
+ {
+ Type t = attr.GetType();
+
+ foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
+ {
+ PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
+ object value = namedArgument.TypedValue.Value;
+
+ if (p.PropertyType.IsEnum)
+ {
+ value = Enum.Parse(p.PropertyType, value.ToString());
+ }
+
+ p.SetValue(attr, value, null);
+ }
+
+ return attr;
+ }
+ }
+ }
+
+ return null;
+#else // ES_BUILD_PCL && PROJECTN
+ throw new ArgumentException(Resources.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
+#endif
+ }
+
+ /// <summary>
+ /// Evaluates if two related "EventSource"-domain types should be considered the same
+ /// </summary>
+ /// <param name="attributeType">The attribute type in the load context - it's associated with the running
+ /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
+ /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
+ /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
+ /// </param>
+ /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
+ private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
+ {
+ return
+ // are these the same type?
+ attributeType == reflectedAttributeType ||
+ // are the full typenames equal?
+ string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
+ // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
+ // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
+ string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
+ attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
+ (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
+#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
+ || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
+#endif
+);
+ }
+
+ private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
+ {
+ // return false for "object" and interfaces
+ if (eventSourceType.BaseType() == null)
+ return null;
+
+ // now go up the inheritance chain until hitting a concrete type ("object" at worse)
+ do
+ {
+ eventSourceType = eventSourceType.BaseType();
+ }
+ while (eventSourceType != null && eventSourceType.IsAbstract());
+
+ if (eventSourceType != null)
+ {
+ if (!allowEventSourceOverride)
+ {
+ if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
+ !reflectionOnly && eventSourceType != typeof(EventSource))
+ return null;
+ }
+ else
+ {
+ if (eventSourceType.Name != "EventSource")
+ return null;
+ }
+ }
+ return eventSourceType;
+ }
+
+ // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
+ // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
+ // at run time. 'source' is the event source to place the descriptors. If it is null,
+ // then the descriptors are not creaed, and just the manifest is generated.
+ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
+ EventManifestOptions flags = EventManifestOptions.None)
+ {
+ ManifestBuilder manifest = null;
+ bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
+ Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
+ byte[] res = null;
+
+ if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
+ return null;
+
+#if DEBUG && ES_BUILD_STANDALONE
+ TestSupport.TestHooks.MaybeThrow(eventSourceType,
+ TestSupport.Category.ManifestError,
+ "EventSource_CreateManifestAndDescriptors",
+ new ArgumentException("EventSource_CreateManifestAndDescriptors"));
+#endif
+
+ try
+ {
+ MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ EventAttribute defaultEventAttribute;
+ int eventId = 1; // The number given to an event that does not have a explicitly given ID.
+ EventMetadata[] eventData = null;
+ Dictionary<string, string> eventsByName = null;
+ if (source != null || (flags & EventManifestOptions.Strict) != 0)
+ {
+ eventData = new EventMetadata[methods.Length + 1];
+ eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
+ }
+
+ // See if we have localization information.
+ ResourceManager resources = null;
+ EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
+ if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
+ resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
+
+ manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
+ resources, flags);
+
+ // Add an entry unconditionally for event ID 0 which will be for a string message.
+ manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
+ manifest.AddEventParameter(typeof(string), "message");
+ manifest.EndEvent();
+
+ // eventSourceType must be sealed and must derive from this EventSource
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
+
+ if (!typeMatch)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
+ }
+ if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
+ }
+ }
+
+ // Collect task, opcode, keyword and channel information
+#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
+#else
+ foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
+#endif
+ {
+ Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
+ if (nestedType != null)
+ {
+ if (eventSourceType.IsAbstract())
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
+ }
+ else
+ {
+ foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
+ {
+ AddProviderEnumKind(manifest, staticField, providerEnumKind);
+ }
+ }
+ }
+ }
+ // ensure we have keywords for the session-filtering reserved bits
+ {
+ manifest.AddKeyword("Session3", (long)0x1000 << 32);
+ manifest.AddKeyword("Session2", (long)0x2000 << 32);
+ manifest.AddKeyword("Session1", (long)0x4000 << 32);
+ manifest.AddKeyword("Session0", (long)0x8000 << 32);
+ }
+
+ if (eventSourceType != typeof(EventSource))
+ {
+ for (int i = 0; i < methods.Length; i++)
+ {
+ MethodInfo method = methods[i];
+ ParameterInfo[] args = method.GetParameters();
+
+ // Get the EventDescriptor (from the Custom attributes)
+ EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
+
+ // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
+ // the only reason of limiting the number of methods considered to be events. This broke a common
+ // design of having event sources implement specific interfaces. To fix this in a compatible way
+ // we will now allow both non-void returning and virtual methods to be Event methods, as long
+ // as they are marked with the [Event] attribute
+ if (/* method.IsVirtual || */ method.IsStatic)
+ {
+ continue;
+ }
+
+ if (eventSourceType.IsAbstract())
+ {
+ if (eventAttribute != null)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
+ }
+ continue;
+ }
+ else if (eventAttribute == null)
+ {
+ // Methods that don't return void can't be events, if they're NOT marked with [Event].
+ // (see Compat comment above)
+ if (method.ReturnType != typeof(void))
+ {
+ continue;
+ }
+
+ // Continue to ignore virtual methods if they do NOT have the [Event] attribute
+ // (see Compat comment above)
+ if (method.IsVirtual)
+ {
+ continue;
+ }
+
+ // If we explicitly mark the method as not being an event, then honor that.
+ if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
+ continue;
+
+ defaultEventAttribute = new EventAttribute(eventId);
+ eventAttribute = defaultEventAttribute;
+ }
+ else if (eventAttribute.EventId <= 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
+ continue; // don't validate anything else for this event
+ }
+ if (method.Name.LastIndexOf('.') >= 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
+ }
+
+ eventId++;
+ string eventName = method.Name;
+
+ if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
+ {
+ // By default pick a task ID derived from the EventID, starting with the highest task number and working back
+ bool noTask = (eventAttribute.Task == EventTask.None);
+ if (noTask)
+ eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
+
+ // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
+ // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
+ if (!eventAttribute.IsOpcodeSet)
+ eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
+
+ // Make the stop opcode have the same task as the start opcode.
+ if (noTask)
+ {
+ if (eventAttribute.Opcode == EventOpcode.Start)
+ {
+ string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
+ if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
+ string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
+ {
+ // Add a task that is just the task name for the start event. This suppress the auto-task generation
+ // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
+ manifest.AddTask(taskName, (int)eventAttribute.Task);
+ }
+ }
+ else if (eventAttribute.Opcode == EventOpcode.Stop)
+ {
+ // Find the start associated with this stop event. We require start to be immediately before the stop
+ int startEventId = eventAttribute.EventId - 1;
+ if (eventData != null && startEventId < eventData.Length)
+ {
+ Contract.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
+ EventMetadata startEventMetadata = eventData[startEventId];
+
+ // If you remove the Stop and add a Start does that name match the Start Event's Name?
+ // Ideally we would throw an error
+ string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
+ if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
+ string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
+ string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
+ {
+
+ // Make the stop event match the start event
+ eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
+ noTask = false;
+ }
+ }
+ if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_StopsFollowStarts"));
+ }
+ }
+ }
+ }
+
+ bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
+ if (!(source != null && source.SelfDescribingEvents))
+ {
+ manifest.StartEvent(eventName, eventAttribute);
+ for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
+ {
+ manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
+ }
+ manifest.EndEvent();
+ }
+
+ if (source != null || (flags & EventManifestOptions.Strict) != 0)
+ {
+ // Do checking for user errors (optional, but not a big deal so we do it).
+ DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
+ // and is not required for the manifest
+ if (eventAttribute.Channel != EventChannel.None)
+ {
+ unchecked
+ {
+ eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong) eventAttribute.Keywords);
+ }
+ }
+#endif
+ string eventKey = "event_" + eventName;
+ string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
+ // overwrite inline message with the localized message
+ if (msg != null) eventAttribute.Message = msg;
+
+ AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
+ }
+ }
+ }
+
+ // Tell the TraceLogging stuff where to start allocating its own IDs.
+ NameInfo.ReserveEventIDsBelow(eventId);
+
+ if (source != null)
+ {
+ TrimEventDescriptors(ref eventData);
+ source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
+#if FEATURE_MANAGED_ETW_CHANNELS
+ source.m_channelData = manifest.GetChannelData();
+#endif
+ }
+
+ // if this is an abstract event source we've already performed all the validation we can
+ if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
+ {
+ bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
+#if FEATURE_MANAGED_ETW_CHANNELS
+ || manifest.GetChannelData().Length > 0
+#endif
+;
+
+ // if the manifest is not needed and we're not requested to validate the event source return early
+ if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
+ return null;
+
+ res = manifest.CreateManifest();
+ }
+ }
+ catch (Exception e)
+ {
+ // if this is a runtime manifest generation let the exception propagate
+ if ((flags & EventManifestOptions.Strict) == 0)
+ throw;
+ // else store it to include it in the Argument exception we raise below
+ exception = e;
+ }
+
+ if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
+ {
+ string msg = String.Empty;
+ if (manifest.Errors.Count > 0)
+ {
+ bool firstError = true;
+ foreach (string error in manifest.Errors)
+ {
+ if (!firstError)
+ msg += Environment.NewLine;
+ firstError = false;
+ msg += error;
+ }
+ }
+ else
+ msg = "Unexpected error: " + exception.Message;
+
+ throw new ArgumentException(msg, exception);
+ }
+
+ return bNeedsManifest ? res : null;
+ }
+
+ private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
+ {
+ // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
+ if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
+ string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ var newargs = new ParameterInfo[args.Length - 1];
+ Array.Copy(args, 1, newargs, 0, args.Length - 1);
+ args = newargs;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
+ // to the manifest.
+ private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
+ {
+ bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
+ Type staticFieldType = staticField.FieldType;
+ if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
+ {
+ if (providerEnumKind != "Opcodes") goto Error;
+ int value = (int)staticField.GetRawConstantValue();
+ manifest.AddOpcode(staticField.Name, value);
+ }
+ else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
+ {
+ if (providerEnumKind != "Tasks") goto Error;
+ int value = (int)staticField.GetRawConstantValue();
+ manifest.AddTask(staticField.Name, value);
+ }
+ else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
+ {
+ if (providerEnumKind != "Keywords") goto Error;
+ ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
+ manifest.AddKeyword(staticField.Name, value);
+ }
+#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
+ {
+ if (providerEnumKind != "Channels") goto Error;
+ var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
+ manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
+ }
+#endif
+ return;
+ Error:
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
+ // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
+ // it is populated if we need to look up message resources
+ private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
+ EventAttribute eventAttribute, ParameterInfo[] eventParameters,
+ bool hasRelatedActivityID)
+ {
+ if (eventData == null || eventData.Length <= eventAttribute.EventId)
+ {
+ EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
+ Array.Copy(eventData, 0, newValues, 0, eventData.Length);
+ eventData = newValues;
+ }
+
+ eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
+ eventAttribute.EventId,
+ eventAttribute.Version,
+#if FEATURE_MANAGED_ETW_CHANNELS
+ (byte)eventAttribute.Channel,
+#else
+ (byte)0,
+#endif
+ (byte)eventAttribute.Level,
+ (byte)eventAttribute.Opcode,
+ (int)eventAttribute.Task,
+ unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
+
+ eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
+ eventData[eventAttribute.EventId].Name = eventName;
+ eventData[eventAttribute.EventId].Parameters = eventParameters;
+ eventData[eventAttribute.EventId].Message = eventAttribute.Message;
+ eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
+ eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
+ // size after all event descriptors have been added.
+ private static void TrimEventDescriptors(ref EventMetadata[] eventData)
+ {
+ int idx = eventData.Length;
+ while (0 < idx)
+ {
+ --idx;
+ if (eventData[idx].Descriptor.EventId != 0)
+ break;
+ }
+ if (eventData.Length - idx > 2) // allow one wasted slot.
+ {
+ EventMetadata[] newValues = new EventMetadata[idx + 1];
+ Array.Copy(eventData, 0, newValues, 0, newValues.Length);
+ eventData = newValues;
+ }
+ }
+
+ // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
+ // when a listener gets attached to a eventSource
+ internal void AddListener(EventListener listener)
+ {
+ lock (EventListener.EventListenersLock)
+ {
+ bool[] enabledArray = null;
+ if (m_eventData != null)
+ enabledArray = new bool[m_eventData.Length];
+ m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
+ listener.OnEventSourceCreated(this);
+ }
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
+ // index for two distinct events etc. Throws exceptions when it finds something wrong.
+ private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
+ EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
+ ManifestBuilder manifest, EventManifestOptions options)
+ {
+ int evtId = eventAttribute.EventId;
+ string evtName = method.Name;
+ int eventArg = GetHelperCallFirstArg(method);
+ if (eventArg >= 0 && evtId != eventArg)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
+ }
+
+ if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
+ }
+
+ // We give a task to things if they don't have one.
+ // TODO this is moderately expensive (N*N). We probably should not even bother....
+ Contract.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
+ for (int idx = 0; idx < eventData.Length; ++idx)
+ {
+ // skip unused Event IDs.
+ if (eventData[idx].Name == null)
+ continue;
+
+ if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TaskOpcodePairReused",
+ evtName, evtId, eventData[idx].Name, idx));
+ // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
+ if ((options & EventManifestOptions.Strict) == 0)
+ break;
+ }
+ }
+
+ // for non-default event opcodes the user must define a task!
+ if (eventAttribute.Opcode != EventOpcode.Info)
+ {
+ bool failure = false;
+ if (eventAttribute.Task == EventTask.None)
+ failure = true;
+ else
+ {
+ // If you have the auto-assigned Task, then you did not explicitly set one.
+ // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
+ // But all other cases we want to catch the omission.
+ var autoAssignedTask = (EventTask)(0xFFFE - evtId);
+ if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
+ failure = true;
+ }
+ if (failure)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
+ }
+ }
+
+ // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
+ // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
+ // by eventRegister.exe)
+ // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
+ // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
+ // {
+ // throw new WarningException(Resources.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
+ // }
+
+ if (eventsByName == null)
+ eventsByName = new Dictionary<string, string>();
+
+ if (eventsByName.ContainsKey(evtName))
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventNameReused", evtName), true);
+ }
+
+ eventsByName[evtName] = evtName;
+ }
+
+ /// <summary>
+ /// This method looks at the IL and tries to pattern match against the standard
+ /// 'boilerplate' event body
+ /// <code>
+ /// { if (Enabled()) WriteEvent(#, ...) }
+ /// </code>
+ /// If the pattern matches, it returns the literal number passed as the first parameter to
+ /// the WriteEvent. This is used to find common user errors (mismatching this
+ /// number with the EventAttribute ID). It is only used for validation.
+ /// </summary>
+ /// <param name="method">The method to probe.</param>
+ /// <returns>The literal value or -1 if the value could not be determined. </returns>
+ [SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
+ static private int GetHelperCallFirstArg(MethodInfo method)
+ {
+#if (!ES_BUILD_PCL && !PROJECTN)
+ // Currently searches for the following pattern
+ //
+ // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
+ // LDARG0
+ // LDC.I4 XXX
+ // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
+ // CALL
+ // NOP // 0 or more times
+ // RET
+ //
+ // If we find this pattern we return the XXX. Otherwise we return -1.
+ (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
+ byte[] instrs = method.GetMethodBody().GetILAsByteArray();
+ int retVal = -1;
+ for (int idx = 0; idx < instrs.Length; )
+ {
+ switch (instrs[idx])
+ {
+ case 0: // NOP
+ case 1: // BREAK
+ case 2: // LDARG_0
+ case 3: // LDARG_1
+ case 4: // LDARG_2
+ case 5: // LDARG_3
+ case 6: // LDLOC_0
+ case 7: // LDLOC_1
+ case 8: // LDLOC_2
+ case 9: // LDLOC_3
+ case 10: // STLOC_0
+ case 11: // STLOC_1
+ case 12: // STLOC_2
+ case 13: // STLOC_3
+ break;
+ case 14: // LDARG_S
+ case 16: // STARG_S
+ idx++;
+ break;
+ case 20: // LDNULL
+ break;
+ case 21: // LDC_I4_M1
+ case 22: // LDC_I4_0
+ case 23: // LDC_I4_1
+ case 24: // LDC_I4_2
+ case 25: // LDC_I4_3
+ case 26: // LDC_I4_4
+ case 27: // LDC_I4_5
+ case 28: // LDC_I4_6
+ case 29: // LDC_I4_7
+ case 30: // LDC_I4_8
+ if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
+ retVal = instrs[idx] - 22;
+ break;
+ case 31: // LDC_I4_S
+ if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
+ retVal = instrs[idx + 1];
+ idx++;
+ break;
+ case 32: // LDC_I4
+ idx += 4;
+ break;
+ case 37: // DUP
+ break;
+ case 40: // CALL
+ idx += 4;
+
+ if (retVal >= 0)
+ {
+ // Is this call just before return?
+ for (int search = idx + 1; search < instrs.Length; search++)
+ {
+ if (instrs[search] == 42) // RET
+ return retVal;
+ if (instrs[search] != 0) // NOP
+ break;
+ }
+ }
+ retVal = -1;
+ break;
+ case 44: // BRFALSE_S
+ case 45: // BRTRUE_S
+ retVal = -1;
+ idx++;
+ break;
+ case 57: // BRFALSE
+ case 58: // BRTRUE
+ retVal = -1;
+ idx += 4;
+ break;
+ case 103: // CONV_I1
+ case 104: // CONV_I2
+ case 105: // CONV_I4
+ case 106: // CONV_I8
+ case 109: // CONV_U4
+ case 110: // CONV_U8
+ break;
+ case 140: // BOX
+ case 141: // NEWARR
+ idx += 4;
+ break;
+ case 162: // STELEM_REF
+ break;
+ case 254: // PREFIX
+ idx++;
+ // Covers the CEQ instructions used in debug code for some reason.
+ if (idx >= instrs.Length || instrs[idx] >= 6)
+ goto default;
+ break;
+ default:
+ /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
+ " at " + idx + " in method " + method.Name); */
+ return -1;
+ }
+ idx++;
+ }
+#endif
+ return -1;
+ }
+
+#if false // This routine is not needed at all, it was used for unit test debugging.
+ [Conditional("DEBUG")]
+ private static void OutputDebugString(string msg)
+ {
+#if !ES_BUILD_PCL
+ msg = msg.TrimEnd('\r', '\n') +
+ string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
+ System.Diagnostics.Debugger.Log(0, null, msg);
+#endif
+ }
+#endif
+
+ /// <summary>
+ /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
+ /// It will do this even if the EventSource is not enabled.
+ /// TODO remove flush parameter it is not used.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ internal void ReportOutOfBandMessage(string msg, bool flush)
+ {
+ try
+ {
+#if (!ES_BUILD_PCL && !PROJECTN)
+ // send message to debugger without delay
+ System.Diagnostics.Debugger.Log(0, null, String.Format("EventSource Error: {0}{1}", msg , Environment.NewLine));
+#endif
+
+ // Send it to all listeners.
+ if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
+ m_outOfBandMessageCount++;
+ else
+ {
+ if (m_outOfBandMessageCount == 16)
+ return;
+ m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
+ msg = "Reached message limit. End of EventSource error messages.";
+ }
+
+ WriteEventString(EventLevel.LogAlways, -1, msg);
+ WriteStringToAllListeners("EventSourceMessage", msg);
+ }
+ catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
+ }
+
+ private EventSourceSettings ValidateSettings(EventSourceSettings settings)
+ {
+ var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
+ EventSourceSettings.EtwSelfDescribingEventFormat;
+ if ((settings & evtFormatMask) == evtFormatMask)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), nameof(settings));
+ }
+
+ // If you did not explicitly ask for manifest, you get self-describing.
+ if ((settings & evtFormatMask) == 0)
+ settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
+ return settings;
+ }
+
+ private bool ThrowOnEventWriteErrors
+ {
+ get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
+ set
+ {
+ if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
+ else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
+ }
+ }
+
+ private bool SelfDescribingEvents
+ {
+ get
+ {
+ Contract.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
+ ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
+ return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
+ }
+ set
+ {
+ if (!value)
+ {
+ m_config |= EventSourceSettings.EtwManifestEventFormat;
+ m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
+ }
+ else
+ {
+ m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
+ m_config &= ~EventSourceSettings.EtwManifestEventFormat;
+ }
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
+ {
+ Contract.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
+
+ for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
+ {
+ if (!sessions[perEventSourceSessionId])
+ continue;
+
+ ActivityFilter af;
+ if (listener == null)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
+ Contract.Assert(etwSession != null);
+ af = etwSession.m_activityFilter;
+ }
+ else
+ {
+ af = listener.m_activityFilter;
+ }
+
+ if (af == null)
+ continue;
+
+ SessionMask m = new SessionMask();
+ m[perEventSourceSessionId] = true;
+
+ foreach (var t in af.GetFilterAsTuple(m_guid))
+ {
+ WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
+ }
+
+ bool participateInSampling = (listener == null) ?
+ m_activityFilteringForETWEnabled[perEventSourceSessionId] :
+ GetDispatcher(listener).m_activityFilteringEnabled;
+ WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
+ perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
+ }
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // private instance state
+ private string m_name; // My friendly name (privided in ctor)
+ internal int m_id; // A small integer that is unique to this instance.
+ private Guid m_guid; // GUID representing the ETW eventSource to the OS.
+ internal volatile EventMetadata[] m_eventData; // None per-event data
+ private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
+
+ private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
+
+ private EventSourceSettings m_config; // configuration information
+
+ private bool m_eventSourceDisposed; // has Dispose been called.
+
+ // Enabling bits
+ private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
+ internal EventLevel m_level; // highest level enabled by any output dispatcher
+ internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
+
+ // Dispatching state
+ internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
+#if FEATURE_MANAGED_ETW
+ private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
+#endif
+ private bool m_completelyInited; // The EventSource constructor has returned without exception.
+ private Exception m_constructionException; // If there was an exception construction, this is it
+ private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
+ private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
+
+ private string[] m_traits; // Used to implement GetTraits
+
+ internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
+ [ThreadStatic]
+ private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
+
+ [ThreadStatic]
+ private static bool m_EventSourceInDecodeObject = false;
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ internal volatile ulong[] m_channelData;
+#endif
+
+#if FEATURE_ACTIVITYSAMPLING
+ private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
+ private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
+ private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
+ internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
+ internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
+ static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
+ // Also used to mark that activity tracing is on for some case
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
+ // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
+ ActivityTracker m_activityTracker;
+ internal const string s_ActivityStartSuffix = "Start";
+ internal const string s_ActivityStopSuffix = "Stop";
+
+ // used for generating GUID from eventsource name
+ private static readonly byte[] namespaceBytes = new byte[] {
+ 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
+ 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
+ };
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Enables specifying event source configuration options to be used in the EventSource constructor.
+ /// </summary>
+ [Flags]
+ public enum EventSourceSettings
+ {
+ /// <summary>
+ /// This specifies none of the special configuration options should be enabled.
+ /// </summary>
+ Default = 0,
+ /// <summary>
+ /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
+ /// </summary>
+ ThrowOnEventWriteErrors = 1,
+ /// <summary>
+ /// Setting this option is a directive to the ETW listener should use manifest-based format when
+ /// firing events. This is the default option when defining a type derived from EventSource
+ /// (using the protected EventSource constructors).
+ /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
+ /// </summary>
+ EtwManifestEventFormat = 4,
+ /// <summary>
+ /// Setting this option is a directive to the ETW listener should use self-describing event format
+ /// when firing events. This is the default option when creating a new instance of the EventSource
+ /// type (using the public EventSource constructors).
+ /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
+ /// </summary>
+ EtwSelfDescribingEventFormat = 8,
+ }
+
+ /// <summary>
+ /// An EventListener represents a target for the events generated by EventSources (that is subclasses
+ /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
+ /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
+ /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
+ /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
+ /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
+ /// longer needed.
+ /// <para>
+ /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
+ /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
+ /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
+ /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
+ /// </para><para>
+ /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
+ /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
+ /// </para><para>
+ /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
+ /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
+ /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
+ /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
+ /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
+ /// </para><para>
+ /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
+ /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
+ /// that eventSource what events that dispatcher will receive.
+ /// </para><para>
+ /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
+ /// override this method to do something useful with the data.
+ /// </para><para>
+ /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
+ /// invariant associated with this callback is that every eventSource gets exactly one
+ /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
+ /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
+ /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
+ /// created.
+ /// </para>
+ /// </summary>
+ public class EventListener : IDisposable
+ {
+ private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
+
+ /// <summary>
+ /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
+ /// This can happen for all existing EventSources when the EventListener is created
+ /// as well as for any EventSources that come into existence after the EventListener
+ /// has been created.
+ ///
+ /// These 'catch up' events are called during the construction of the EventListener.
+ /// Subclasses need to be prepared for that.
+ ///
+ /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
+ /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
+ /// </summary>
+ public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
+ {
+ add
+ {
+ CallBackForExistingEventSources(false, value);
+
+ this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
+ }
+ remove
+ {
+ this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
+ }
+ }
+
+ /// <summary>
+ /// This event is raised whenever an event has been written by a EventSource for which
+ /// the EventListener has enabled events.
+ /// </summary>
+ public event EventHandler<EventWrittenEventArgs> EventWritten;
+
+ /// <summary>
+ /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
+ /// them on).
+ /// </summary>
+ public EventListener()
+ {
+ // This will cause the OnEventSourceCreated callback to fire.
+ CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener(this) );
+ }
+
+ /// <summary>
+ /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
+ /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
+ /// is the only way to actually make the listen die. Thus it is important that users of EventListener
+ /// call Dispose when they are done with their logging.
+ /// </summary>
+#if ES_BUILD_STANDALONE
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
+#endif
+ public virtual void Dispose()
+ {
+ lock (EventListenersLock)
+ {
+ if (s_Listeners != null)
+ {
+ if (this == s_Listeners)
+ {
+ EventListener cur = s_Listeners;
+ s_Listeners = this.m_Next;
+ RemoveReferencesToListenerInEventSources(cur);
+ }
+ else
+ {
+ // Find 'this' from the s_Listeners linked list.
+ EventListener prev = s_Listeners;
+ for (; ; )
+ {
+ EventListener cur = prev.m_Next;
+ if (cur == null)
+ break;
+ if (cur == this)
+ {
+ // Found our Listener, remove references to to it in the eventSources
+ prev.m_Next = cur.m_Next; // Remove entry.
+ RemoveReferencesToListenerInEventSources(cur);
+ break;
+ }
+ prev = cur;
+ }
+ }
+ }
+ Validate();
+ }
+ }
+ // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
+ // 'cleanup' associated with this object
+
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
+ ///
+ /// This call never has an effect on other EventListeners.
+ ///
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level)
+ {
+ EnableEvents(eventSource, level, EventKeywords.None);
+ }
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
+ /// matching any of the bits in 'matchAnyKeyword'.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
+ /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
+ {
+ EnableEvents(eventSource, level, matchAnyKeyword, null);
+ }
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
+ /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
+ /// effect passing additional 'key-value' arguments 'arguments' might have.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
+ /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
+ {
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+ Contract.EndContractBlock();
+
+ eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
+ }
+ /// <summary>
+ /// Disables all events coming from eventSource identified by 'eventSource'.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void DisableEvents(EventSource eventSource)
+ {
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+ Contract.EndContractBlock();
+
+ eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
+ }
+
+ /// <summary>
+ /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
+ /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
+ /// it useful to store additional information about each eventSource connected to it,
+ /// and EventSourceIndex allows this extra information to be efficiently stored in a
+ /// (growable) array (eg List(T)).
+ /// </summary>
+ public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
+
+ /// <summary>
+ /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
+ /// This can happen for all existing EventSources when the EventListener is created
+ /// as well as for any EventSources that come into existence after the EventListener
+ /// has been created.
+ ///
+ /// These 'catch up' events are called during the construction of the EventListener.
+ /// Subclasses need to be prepared for that.
+ ///
+ /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
+ /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
+ /// </summary>
+ /// <param name="eventSource"></param>
+ internal protected virtual void OnEventSourceCreated(EventSource eventSource)
+ {
+ EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
+ if(callBack != null)
+ {
+ EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
+ args.EventSource = eventSource;
+ callBack(this, args);
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever an event has been written by a EventSource for which
+ /// the EventListener has enabled events.
+ /// </summary>
+ /// <param name="eventData"></param>
+ internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
+ if (callBack != null)
+ {
+ callBack(this, eventData);
+ }
+ }
+
+
+ #region private
+ /// <summary>
+ /// This routine adds newEventSource to the global list of eventSources, it also assigns the
+ /// ID to the eventSource (which is simply the ordinal in the global list).
+ ///
+ /// EventSources currently do not pro-actively remove themselves from this list. Instead
+ /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
+ /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
+ /// that are in the list). This seems OK since the expectation is that EventSources
+ /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
+ /// global variables).
+ /// </summary>
+ /// <param name="newEventSource"></param>
+ internal static void AddEventSource(EventSource newEventSource)
+ {
+ lock (EventListenersLock)
+ {
+ if (s_EventSources == null)
+ s_EventSources = new List<WeakReference>(2);
+
+ if (!s_EventSourceShutdownRegistered)
+ {
+ s_EventSourceShutdownRegistered = true;
+#if (!ES_BUILD_PCL && !FEATURE_CORECLR && !PROJECTN)
+ AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
+ AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
+#endif
+ }
+
+
+ // Periodically search the list for existing entries to reuse, this avoids
+ // unbounded memory use if we keep recycling eventSources (an unlikely thing).
+ int newIndex = -1;
+ if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
+ {
+ int i = s_EventSources.Count; // Work from the top down.
+ while (0 < i)
+ {
+ --i;
+ WeakReference weakRef = s_EventSources[i];
+ if (!weakRef.IsAlive)
+ {
+ newIndex = i;
+ weakRef.Target = newEventSource;
+ break;
+ }
+ }
+ }
+ if (newIndex < 0)
+ {
+ newIndex = s_EventSources.Count;
+ s_EventSources.Add(new WeakReference(newEventSource));
+ }
+ newEventSource.m_id = newIndex;
+
+ // Add every existing dispatcher to the new EventSource
+ for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
+ newEventSource.AddListener(listener);
+
+ Validate();
+ }
+ }
+
+ // Whenver we have async callbacks from native code, there is an ugly issue where
+ // during .NET shutdown native code could be calling the callback, but the CLR
+ // has already prohibited callbacks to managed code in the appdomain, causing the CLR
+ // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
+ // such callbacks on process shutdown or appdomain so that unmanaged code will never
+ // do this. This is what this callback is for.
+ // See bug 724140 for more
+ private static void DisposeOnShutdown(object sender, EventArgs e)
+ {
+ lock(EventListenersLock)
+ {
+ foreach (var esRef in s_EventSources)
+ {
+ EventSource es = esRef.Target as EventSource;
+ if (es != null)
+ es.Dispose();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
+ /// eventSources in the appdomain.
+ ///
+ /// The EventListenersLock must be held before calling this routine.
+ /// </summary>
+ private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ // Foreach existing EventSource in the appdomain
+ foreach (WeakReference eventSourceRef in s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null)
+ {
+ // Is the first output dispatcher the dispatcher we are removing?
+ if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
+ eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
+ else
+ {
+ // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
+ EventDispatcher prev = eventSource.m_Dispatchers;
+ for (; ; )
+ {
+ EventDispatcher cur = prev.m_Next;
+ if (cur == null)
+ {
+ Contract.Assert(false, "EventSource did not have a registered EventListener!");
+ break;
+ }
+ if (cur.m_Listener == listenerToRemove)
+ {
+ prev.m_Next = cur.m_Next; // Remove entry.
+ break;
+ }
+ prev = cur;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks internal consistency of EventSources/Listeners.
+ /// </summary>
+ [Conditional("DEBUG")]
+ internal static void Validate()
+ {
+ lock (EventListenersLock)
+ {
+ // Get all listeners
+ Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
+ EventListener cur = s_Listeners;
+ while (cur != null)
+ {
+ allListeners.Add(cur, true);
+ cur = cur.m_Next;
+ }
+
+ // For all eventSources
+ int id = -1;
+ foreach (WeakReference eventSourceRef in s_EventSources)
+ {
+ id++;
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource == null)
+ continue;
+ Contract.Assert(eventSource.m_id == id, "Unexpected event source ID.");
+
+ // None listeners on eventSources exist in the dispatcher list.
+ EventDispatcher dispatcher = eventSource.m_Dispatchers;
+ while (dispatcher != null)
+ {
+ Contract.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
+ dispatcher = dispatcher.m_Next;
+ }
+
+ // Every dispatcher is on Dispatcher List of every eventSource.
+ foreach (EventListener listener in allListeners.Keys)
+ {
+ dispatcher = eventSource.m_Dispatchers;
+ for (; ; )
+ {
+ Contract.Assert(dispatcher != null, "Listener is not on all eventSources.");
+ if (dispatcher.m_Listener == listener)
+ break;
+ dispatcher = dispatcher.m_Next;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
+ /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
+ /// the lock object)
+ /// </summary>
+ internal static object EventListenersLock
+ {
+ get
+ {
+ if (s_EventSources == null)
+ Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
+ return s_EventSources;
+ }
+ }
+
+ private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
+ {
+ lock (EventListenersLock)
+ {
+ // Disallow creating EventListener reentrancy.
+ if (s_CreatingListener)
+ {
+ throw new InvalidOperationException(Resources.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
+ }
+
+ try
+ {
+ s_CreatingListener = true;
+
+ if (addToListenersList)
+ {
+ // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
+ // Those added sources see this listener.
+ this.m_Next = s_Listeners;
+ s_Listeners = this;
+ }
+
+ // Find all existing eventSources call OnEventSourceCreated to 'catchup'
+ // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
+ // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
+ // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
+ // is created.
+ WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
+
+ for (int i = 0; i < eventSourcesSnapshot.Length; i++)
+ {
+ WeakReference eventSourceRef = eventSourcesSnapshot[i];
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null)
+ {
+ EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
+ args.EventSource = eventSource;
+ callback(this, args);
+ }
+ }
+
+ Validate();
+ }
+ finally
+ {
+ s_CreatingListener = false;
+ }
+ }
+
+ }
+
+ // Instance fields
+ internal volatile EventListener m_Next; // These form a linked list in s_Listeners
+#if FEATURE_ACTIVITYSAMPLING
+ internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // static fields
+
+ /// <summary>
+ /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
+ /// from this list. Note that EventSources point to their listener but NOT the reverse.
+ /// </summary>
+ internal static EventListener s_Listeners;
+ /// <summary>
+ /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
+ /// remove themselves from this list this is a weak list and the GC that removes them may
+ /// not have happened yet. Thus it can contain event sources that are dead (thus you have
+ /// to filter those out.
+ /// </summary>
+ internal static List<WeakReference> s_EventSources;
+
+ /// <summary>
+ /// Used to disallow reentrancy.
+ /// </summary>
+ private static bool s_CreatingListener = false;
+
+ /// <summary>
+ /// Used to register AD/Process shutdown callbacks.
+ /// </summary>
+ private static bool s_EventSourceShutdownRegistered = false;
+ #endregion
+ }
+
+ /// <summary>
+ /// Passed to the code:EventSource.OnEventCommand callback
+ /// </summary>
+ public class EventCommandEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets the command for the callback.
+ /// </summary>
+ public EventCommand Command { get; internal set; }
+
+ /// <summary>
+ /// Gets the arguments for the callback.
+ /// </summary>
+ public IDictionary<String, String> Arguments { get; internal set; }
+
+ /// <summary>
+ /// Enables the event that has the specified identifier.
+ /// </summary>
+ /// <param name="eventId">Event ID of event to be enabled</param>
+ /// <returns>true if eventId is in range</returns>
+ public bool EnableEvent(int eventId)
+ {
+ if (Command != EventCommand.Enable && Command != EventCommand.Disable)
+ throw new InvalidOperationException();
+ return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
+ }
+
+ /// <summary>
+ /// Disables the event that have the specified identifier.
+ /// </summary>
+ /// <param name="eventId">Event ID of event to be disabled</param>
+ /// <returns>true if eventId is in range</returns>
+ public bool DisableEvent(int eventId)
+ {
+ if (Command != EventCommand.Enable && Command != EventCommand.Disable)
+ throw new InvalidOperationException();
+ return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
+ }
+
+ #region private
+
+ internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
+ EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
+ {
+ this.Command = command;
+ this.Arguments = arguments;
+ this.eventSource = eventSource;
+ this.listener = listener;
+ this.perEventSourceSessionId = perEventSourceSessionId;
+ this.etwSessionId = etwSessionId;
+ this.enable = enable;
+ this.level = level;
+ this.matchAnyKeyword = matchAnyKeyword;
+ }
+
+ internal EventSource eventSource;
+ internal EventDispatcher dispatcher;
+
+ // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
+ internal EventListener listener;
+ internal int perEventSourceSessionId;
+ internal int etwSessionId;
+ internal bool enable;
+ internal EventLevel level;
+ internal EventKeywords matchAnyKeyword;
+ internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
+
+ #endregion
+ }
+
+ /// <summary>
+ /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
+ /// </summary>
+ public class EventSourceCreatedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The EventSource that is attaching to the listener.
+ /// </summary>
+ public EventSource EventSource
+ {
+ get;
+ internal set;
+ }
+ }
+
+ /// <summary>
+ /// EventWrittenEventArgs is passed to the user-provided override for
+ /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
+ /// </summary>
+ public class EventWrittenEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The name of the event.
+ /// </summary>
+ public string EventName
+ {
+ get
+ {
+ if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
+ {
+ return m_eventName;
+ }
+ else
+ return m_eventSource.m_eventData[EventId].Name;
+ }
+ internal set
+ {
+ m_eventName = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the event ID for the event that was written.
+ /// </summary>
+ public int EventId { get; internal set; }
+
+ /// <summary>
+ /// Gets the activity ID for the thread on which the event was written.
+ /// </summary>
+ public Guid ActivityId
+ {
+ [System.Security.SecurityCritical]
+ get { return EventSource.CurrentThreadActivityId; }
+ }
+
+ /// <summary>
+ /// Gets the related activity ID if one was specified when the event was written.
+ /// </summary>
+ public Guid RelatedActivityId
+ {
+ [System.Security.SecurityCritical]
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Gets the payload for the event.
+ /// </summary>
+ public ReadOnlyCollection<Object> Payload { get; internal set; }
+
+ /// <summary>
+ /// Gets the payload argument names.
+ /// </summary>
+ public ReadOnlyCollection<string> PayloadNames
+ {
+ get
+ {
+ // For contract based events we create the list lazily.
+ if (m_payloadNames == null)
+ {
+ // Self described events are identified by id -1.
+ Contract.Assert(EventId != -1);
+
+ var names = new List<string>();
+ foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
+ {
+ names.Add(parameter.Name);
+ }
+ m_payloadNames = new ReadOnlyCollection<string>(names);
+ }
+
+ return m_payloadNames;
+ }
+
+ internal set
+ {
+ m_payloadNames = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the event source object.
+ /// </summary>
+ public EventSource EventSource { get { return m_eventSource; } }
+
+ /// <summary>
+ /// Gets the keywords for the event.
+ /// </summary>
+ public EventKeywords Keywords
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_keywords;
+
+ return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
+ }
+ }
+
+ /// <summary>
+ /// Gets the operation code for the event.
+ /// </summary>
+ public EventOpcode Opcode
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_opcode;
+ return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
+ }
+ }
+
+ /// <summary>
+ /// Gets the task for the event.
+ /// </summary>
+ public EventTask Task
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return EventTask.None;
+
+ return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
+ }
+ }
+
+ /// <summary>
+ /// Any provider/user defined options associated with the event.
+ /// </summary>
+ public EventTags Tags
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_tags;
+ return m_eventSource.m_eventData[EventId].Tags;
+ }
+ }
+
+ /// <summary>
+ /// Gets the message for the event.
+ /// </summary>
+ public string Message
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_message;
+ else
+ return m_eventSource.m_eventData[EventId].Message;
+ }
+ internal set
+ {
+ m_message = value;
+ }
+ }
+
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Gets the channel for the event.
+ /// </summary>
+ public EventChannel Channel
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return EventChannel.None;
+ return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
+ }
+ }
+#endif
+
+ /// <summary>
+ /// Gets the version of the event.
+ /// </summary>
+ public byte Version
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return 0;
+ return m_eventSource.m_eventData[EventId].Descriptor.Version;
+ }
+ }
+
+ /// <summary>
+ /// Gets the level for the event.
+ /// </summary>
+ public EventLevel Level
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_level;
+ return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
+ }
+ }
+
+ #region private
+ internal EventWrittenEventArgs(EventSource eventSource)
+ {
+ m_eventSource = eventSource;
+ }
+ private string m_message;
+ private string m_eventName;
+ private EventSource m_eventSource;
+ private ReadOnlyCollection<string> m_payloadNames;
+ internal EventTags m_tags;
+ internal EventOpcode m_opcode;
+ internal EventLevel m_level;
+ internal EventKeywords m_keywords;
+ #endregion
+ }
+
+ /// <summary>
+ /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class EventSourceAttribute : Attribute
+ {
+ /// <summary>
+ /// Overrides the ETW name of the event source (which defaults to the class name)
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
+ /// except when upgrading existing ETW providers to using event sources.
+ /// </summary>
+ public string Guid { get; set; }
+
+ /// <summary>
+ /// <para>
+ /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
+ /// can be localized to several languages if desired. This works by creating a ResX style string table
+ /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
+ /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
+ /// resources. This name is the value of the LocalizationResources property.
+ /// </para><para>
+ /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
+ /// using the following resource naming scheme
+ /// </para>
+ /// <para>* event_EVENTNAME</para>
+ /// <para>* task_TASKNAME</para>
+ /// <para>* keyword_KEYWORDNAME</para>
+ /// <para>* map_MAPNAME</para>
+ /// <para>
+ /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
+ /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
+ /// which represent the payload values.
+ /// </para>
+ /// </summary>
+ public string LocalizationResources { get; set; }
+ }
+
+ /// <summary>
+ /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
+ /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
+ /// name of the method and its signature to generate basic schema information for the event. The
+ /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
+ /// desired.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class EventAttribute : Attribute
+ {
+ /// <summary>Construct an EventAttribute with specified eventId</summary>
+ /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
+ public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
+ /// <summary>Event's ID</summary>
+ public int EventId { get; private set; }
+ /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
+ public EventLevel Level { get; set; }
+ /// <summary>Event's keywords: allows classification of events by "categories"</summary>
+ public EventKeywords Keywords { get; set; }
+ /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
+ public EventOpcode Opcode
+ {
+ get
+ {
+ return m_opcode;
+ }
+ set
+ {
+ this.m_opcode = value;
+ this.m_opcodeSet = true;
+ }
+ }
+
+ internal bool IsOpcodeSet
+ {
+ get
+ {
+ return m_opcodeSet;
+ }
+ }
+
+ /// <summary>Event's task: allows logical grouping of events</summary>
+ public EventTask Task { get; set; }
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
+ public EventChannel Channel { get; set; }
+#endif
+ /// <summary>Event's version</summary>
+ public byte Version { get; set; }
+
+ /// <summary>
+ /// This can be specified to enable formatting and localization of the event's payload. You can
+ /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
+ /// with the 'ToString()' of the corresponding part of the event payload.
+ /// </summary>
+ public string Message { get; set; }
+
+ /// <summary>
+ /// User defined options associated with the event. These do not have meaning to the EventSource but
+ /// are passed through to listeners which given them semantics.
+ /// </summary>
+ public EventTags Tags { get; set; }
+
+ /// <summary>
+ /// Allows fine control over the Activity IDs generated by start and stop events
+ /// </summary>
+ public EventActivityOptions ActivityOptions { get; set; }
+
+ #region private
+ EventOpcode m_opcode;
+ private bool m_opcodeSet;
+ #endregion
+ }
+
+ /// <summary>
+ /// By default all instance methods in a class that subclasses code:EventSource that and return
+ /// void are assumed to be methods that generate an event. This default can be overridden by specifying
+ /// the code:NonEventAttribute
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class NonEventAttribute : Attribute
+ {
+ /// <summary>
+ /// Constructs a default NonEventAttribute
+ /// </summary>
+ public NonEventAttribute() { }
+ }
+
+ // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
+ /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
+ /// <code>
+ /// public static class Channels
+ /// {
+ /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
+ /// public const EventChannel Admin = (EventChannel)16;
+ ///
+ /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
+ /// public const EventChannel Operational = (EventChannel)17;
+ /// }
+ /// </code>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field)]
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ public
+#endif
+ class EventChannelAttribute : Attribute
+ {
+ /// <summary>
+ /// Specified whether the channel is enabled by default
+ /// </summary>
+ public bool Enabled { get; set; }
+
+ /// <summary>
+ /// Legal values are in EventChannelType
+ /// </summary>
+ public EventChannelType EventChannelType { get; set; }
+
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Specifies the isolation for the channel
+ /// </summary>
+ public EventChannelIsolation Isolation { get; set; }
+
+ /// <summary>
+ /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
+ /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
+ /// </summary>
+ public string Access { get; set; }
+
+ /// <summary>
+ /// Allows importing channels defined in external manifests
+ /// </summary>
+ public string ImportChannel { get; set; }
+#endif
+
+ // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
+ // public string Name { get; set; }
+ }
+
+ /// <summary>
+ /// Allowed channel types
+ /// </summary>
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ public
+#endif
+ enum EventChannelType
+ {
+ /// <summary>The admin channel</summary>
+ Admin = 1,
+ /// <summary>The operational channel</summary>
+ Operational,
+ /// <summary>The Analytic channel</summary>
+ Analytic,
+ /// <summary>The debug channel</summary>
+ Debug,
+ }
+
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
+ /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
+ /// access permissions for the channel and backing file.
+ /// </summary>
+ public
+ enum EventChannelIsolation
+ {
+ /// <summary>
+ /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
+ /// </summary>
+ Application = 1,
+ /// <summary>
+ /// All channels that specify System isolation use the same ETW session
+ /// </summary>
+ System,
+ /// <summary>
+ /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
+ /// Using Custom isolation lets you control the access permissions for the channel and backing file.
+ /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
+ /// </summary>
+ Custom,
+ }
+#endif
+#endif
+
+ /// <summary>
+ /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
+ /// </summary>
+ public enum EventCommand
+ {
+ /// <summary>
+ /// Update EventSource state
+ /// </summary>
+ Update = 0,
+ /// <summary>
+ /// Request EventSource to generate and send its manifest
+ /// </summary>
+ SendManifest = -1,
+ /// <summary>
+ /// Enable event
+ /// </summary>
+ Enable = -2,
+ /// <summary>
+ /// Disable event
+ /// </summary>
+ Disable = -3
+ };
+
+
+ #region private classes
+
+#if FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// ActivityFilter is a helper structure that is used to keep track of run-time state
+ /// associated with activity filtering. It is 1-1 with EventListeners (logically
+ /// every listener has one of these, however we actually allocate them lazily), as well
+ /// as 1-to-1 with tracing-aware EtwSessions.
+ ///
+ /// This structure also keeps track of the sampling counts associated with 'trigger'
+ /// events. Because these trigger events are rare, and you typically only have one of
+ /// them, we store them here as a linked list.
+ /// </summary>
+ internal sealed class ActivityFilter : IDisposable
+ {
+ /// <summary>
+ /// Disable all activity filtering for the listener associated with 'filterList',
+ /// (in the session associated with it) that is triggered by any event in 'source'.
+ /// </summary>
+ public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+
+ if (filterList == null)
+ return;
+
+ ActivityFilter cur;
+ // Remove it from anywhere in the list (except the first element, which has to
+ // be treated specially)
+ ActivityFilter prev = filterList;
+ cur = prev.m_next;
+ while (cur != null)
+ {
+ if (cur.m_providerGuid == source.Guid)
+ {
+ // update TriggersActivityTracking bit
+ if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
+ --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
+
+ // Remove it from the linked list.
+ prev.m_next = cur.m_next;
+ // dispose of the removed node
+ cur.Dispose();
+ // update cursor
+ cur = prev.m_next;
+ }
+ else
+ {
+ // update cursors
+ prev = cur;
+ cur = prev.m_next;
+ }
+ }
+
+ // Sadly we have to treat the first element specially in linked list removal in C#
+ if (filterList.m_providerGuid == source.Guid)
+ {
+ // update TriggersActivityTracking bit
+ if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
+ --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
+
+ // We are the first element in the list.
+ var first = filterList;
+ filterList = first.m_next;
+ // dispose of the removed node
+ first.Dispose();
+ }
+ // the above might have removed the one ActivityFilter in the session that contains the
+ // cleanup delegate; re-create the delegate if needed
+ if (filterList != null)
+ {
+ EnsureActivityCleanupDelegate(filterList);
+ }
+ }
+
+ /// <summary>
+ /// Currently this has "override" semantics. We first disable all filters
+ /// associated with 'source', and next we add new filters for each entry in the
+ /// string 'startEvents'. participateInSampling specifies whether non-startEvents
+ /// always trigger or only trigger when current activity is 'active'.
+ /// </summary>
+ public static void UpdateFilter(
+ ref ActivityFilter filterList,
+ EventSource source,
+ int perEventSourceSessionId,
+ string startEvents)
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+
+ // first remove all filters associated with 'source'
+ DisableFilter(ref filterList, source);
+
+ if (!string.IsNullOrEmpty(startEvents))
+ {
+ // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
+ // The Event may be specified by name or by ID. Errors in parsing such a pair
+ // result in the error being reported to the listeners, and the pair being ignored.
+ // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
+ // we should initiate activity tracing once every 1000 events, *and* for event ID 12
+ // we should initiate activity tracing once every 10 events.
+ string[] activityFilterStrings = startEvents.Split(' ');
+
+ for (int i = 0; i < activityFilterStrings.Length; ++i)
+ {
+ string activityFilterString = activityFilterStrings[i];
+ int sampleFreq = 1;
+ int eventId = -1;
+ int colonIdx = activityFilterString.IndexOf(':');
+ if (colonIdx < 0)
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
+ activityFilterString, false);
+ // ignore failure...
+ continue;
+ }
+ string sFreq = activityFilterString.Substring(colonIdx + 1);
+ if (!int.TryParse(sFreq, out sampleFreq))
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
+ continue;
+ }
+ activityFilterString = activityFilterString.Substring(0, colonIdx);
+ if (!int.TryParse(activityFilterString, out eventId))
+ {
+ // reset eventId
+ eventId = -1;
+ // see if it's an event name
+ for (int j = 0; j < source.m_eventData.Length; j++)
+ {
+ EventSource.EventMetadata[] ed = source.m_eventData;
+ if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
+ string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ eventId = ed[j].Descriptor.EventId;
+ break;
+ }
+ }
+ }
+ if (eventId < 0 || eventId >= source.m_eventData.Length)
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
+ continue;
+ }
+ EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
+ /// </summary>
+ public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
+ {
+ for (var af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
+ return af;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Returns a session mask representing all sessions in which the activity
+ /// associated with the current thread is allowed through the activity filter.
+ /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
+ /// most of the time this is false as you can guarentee this event is NOT a
+ /// triggering event. If 'triggeringEvent' is true, then it checks the
+ /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
+ /// a trigger. If so it activates the current activity.
+ ///
+ /// If 'childActivityID' is present, it will be added to the active set if the
+ /// current activity is active.
+ /// </summary>
+ [SecurityCritical]
+ unsafe public static bool PassesActivityFilter(
+ ActivityFilter filterList,
+ Guid* childActivityID,
+ bool triggeringEvent,
+ EventSource source,
+ int eventId)
+ {
+ Contract.Assert(filterList != null && filterList.m_activeActivities != null);
+ bool shouldBeLogged = false;
+ if (triggeringEvent)
+ {
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
+ {
+ // Update the sampling count with wrap-around
+ int curSampleCount, newSampleCount;
+ do
+ {
+ curSampleCount = af.m_curSampleCount;
+ if (curSampleCount <= 1)
+ newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
+ else
+ newSampleCount = curSampleCount - 1;
+ }
+ while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
+ // If we hit zero, then start tracking the activity.
+ if (curSampleCount <= 1)
+ {
+ Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
+ Tuple<Guid, int> startId;
+ // only add current activity if it's not already a root activity
+ if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
+ {
+ // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
+ shouldBeLogged = true;
+ af.m_activeActivities[currentActivityId] = Environment.TickCount;
+ af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
+ }
+ }
+ else
+ {
+ // a start event following a triggering start event
+ Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
+ Tuple<Guid, int> startId;
+ // only remove current activity if we added it
+ if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
+ startId.Item1 == source.Guid && startId.Item2 == eventId)
+ {
+ // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
+ // remove activity only from current logging scope (af)
+ int dummy;
+ af.m_activeActivities.TryRemove(currentActivityId, out dummy);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ var activeActivities = GetActiveActivities(filterList);
+ if (activeActivities != null)
+ {
+ // if we hadn't already determined this should be logged, test further
+ if (!shouldBeLogged)
+ {
+ shouldBeLogged = !activeActivities.IsEmpty &&
+ activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
+ }
+ if (shouldBeLogged && childActivityID != null &&
+ ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
+ {
+ FlowActivityIfNeeded(filterList, null, childActivityID);
+ // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
+ }
+ }
+ // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
+ return shouldBeLogged;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ public static bool IsCurrentActivityActive(ActivityFilter filterList)
+ {
+ var activeActivities = GetActiveActivities(filterList);
+ if (activeActivities != null &&
+ activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
+ return true;
+
+ return false;
+ }
+
+ /// <summary>
+ /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
+ /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
+ /// value for 'currentActivityid' is an indication tha caller has already verified
+ /// that the current activity is active.
+ /// </summary>
+ [SecurityCritical]
+ unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
+ {
+ Contract.Assert(childActivityID != null);
+
+ var activeActivities = GetActiveActivities(filterList);
+ Contract.Assert(activeActivities != null);
+
+ // take currentActivityId == null to mean we *know* the current activity is "active"
+ if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
+ return;
+
+ if (activeActivities.Count > MaxActivityTrackCount)
+ {
+ TrimActiveActivityStore(activeActivities);
+ // make sure current activity is still in the set:
+ activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
+ }
+ // add child activity to list of actives
+ activeActivities[*childActivityID] = Environment.TickCount;
+
+ }
+
+ /// <summary>
+ /// </summary>
+ public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
+ {
+ for (var af = activityFilter; af != null; af = af.m_next)
+ {
+ if ((sourceGuid == af.m_providerGuid) &&
+ (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
+ ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
+ {
+ // we could be more precise here, if we tracked 'anykeywords' per session
+ unchecked
+ {
+ source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
+ /// associated with 'this' ActivityFilter list, return configured sequence of
+ /// [eventId, sampleFreq] pairs that defines the sampling policy.
+ /// </summary>
+ public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
+ {
+ for (ActivityFilter af = this; af != null; af = af.m_next)
+ {
+ if (af.m_providerGuid == sourceGuid)
+ yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
+ }
+ }
+
+ /// <summary>
+ /// The cleanup being performed consists of removing the m_myActivityDelegate from
+ /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
+ /// </summary>
+ public void Dispose()
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
+ // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
+ // during the finalization of the ActivityFilter
+ if (m_myActivityDelegate != null)
+ {
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
+ m_myActivityDelegate = null;
+ }
+ }
+
+ #region private
+
+ /// <summary>
+ /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
+ /// 'samplingFreq' times the event fires. You can have several of these forming a
+ /// linked list.
+ /// </summary>
+ private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
+ {
+ m_providerGuid = source.Guid;
+ m_perEventSourceSessionId = perEventSourceSessionId;
+ m_eventId = eventId;
+ m_samplingFreq = samplingFreq;
+ m_next = existingFilter;
+
+ Contract.Assert(existingFilter == null ||
+ (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
+
+ // if this is the first filter we add for this session, we need to create a new
+ // table of activities. m_activeActivities is common across EventSources in the same
+ // session
+ ConcurrentDictionary<Guid, int> activeActivities = null;
+ if (existingFilter == null ||
+ (activeActivities = GetActiveActivities(existingFilter)) == null)
+ {
+ m_activeActivities = new ConcurrentDictionary<Guid, int>();
+ m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
+
+ // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
+ m_myActivityDelegate = GetActivityDyingDelegate(this);
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
+ }
+ else
+ {
+ m_activeActivities = activeActivities;
+ m_rootActiveActivities = existingFilter.m_rootActiveActivities;
+ }
+
+ }
+
+ /// <summary>
+ /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
+ /// activity-removing delegate for the listener/session associated with 'filterList'.
+ /// </summary>
+ private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
+ {
+ if (filterList == null)
+ return;
+
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_myActivityDelegate != null)
+ return;
+ }
+
+ // we didn't find a delegate
+ filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
+ }
+
+ /// <summary>
+ /// Builds the delegate to be called when an activity is dying. This is responsible
+ /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
+ /// This gets "added" to EventSource.s_activityDying and ends up being called from
+ /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
+ /// </summary>
+ /// <returns>The delegate to be called when an activity is dying</returns>
+ private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
+ {
+ return (Guid oldActivity) =>
+ {
+ int dummy;
+ filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
+ Tuple<Guid, int> dummyTuple;
+ filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
+ };
+ }
+
+ /// <summary>
+ /// Enables activity filtering for the listener associated with 'filterList', triggering on
+ /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
+ ///
+ /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
+ /// activitySampling if something else triggered).
+ /// </summary>
+ /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
+ private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ Contract.Assert(samplingFreq > 0);
+ Contract.Assert(eventId >= 0);
+
+ filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
+
+ // Mark the 'quick Check' that indicates this is a trigger event.
+ // If eventId is out of range then this mark is not done which has the effect of ignoring
+ // the trigger.
+ if (0 <= eventId && eventId < source.m_eventData.Length)
+ ++source.m_eventData[eventId].TriggersActivityTracking;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Normally this code never runs, it is here just to prevent run-away resource usage.
+ /// </summary>
+ private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
+ {
+ if (activities.Count > MaxActivityTrackCount)
+ {
+ // Remove half of the oldest activity ids.
+ var keyValues = activities.ToArray();
+ var tickNow = Environment.TickCount;
+
+ // Sort by age, taking into account wrap-around. As long as x and y are within
+ // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
+ // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
+ Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
+ for (int i = 0; i < keyValues.Length / 2; i++)
+ {
+ int dummy;
+ activities.TryRemove(keyValues[i].Key, out dummy);
+ }
+ }
+ }
+
+ private static ConcurrentDictionary<Guid, int> GetActiveActivities(
+ ActivityFilter filterList)
+ {
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_activeActivities != null)
+ return af.m_activeActivities;
+ }
+ return null;
+ }
+
+ // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
+ // in the m_next list. The 'int' value in the m_activities set is a timestamp
+ // (Environment.TickCount) of when the entry was put in the system and is used to
+ // remove 'old' entries that if the set gets too big.
+ ConcurrentDictionary<Guid, int> m_activeActivities;
+
+ // m_rootActiveActivities holds the "root" active activities, i.e. the activities
+ // that were marked as active because a Start event fired on them. We need to keep
+ // track of these to enable sampling in the scenario of an app's main thread that
+ // never explicitly sets distinct activity IDs as it executes. To handle these
+ // situations we manufacture a Guid from the thread's ID, and:
+ // (a) we consider the firing of a start event when the sampling counter reaches
+ // zero to mark the beginning of an interesting activity, and
+ // (b) we consider the very next firing of the same start event to mark the
+ // ending of that activity.
+ // We use a ConcurrentDictionary to avoid taking explicit locks.
+ // The key (a guid) represents the activity ID of the root active activity
+ // The value is made up of the Guid of the event provider and the eventId of
+ // the start event.
+ ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
+ Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
+ int m_eventId; // triggering event
+ int m_samplingFreq; // Counter reset to this when it hits 0
+ int m_curSampleCount; // We count down to 0 and then activate the activity.
+ int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
+
+ const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
+
+ ActivityFilter m_next; // We create a linked list of these
+ Action<Guid> m_myActivityDelegate;
+ #endregion
+ };
+
+
+ /// <summary>
+ /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
+ /// are limited to 8 concurrent sessions per machine (currently) we're going to store
+ /// the active ones in a singly linked list.
+ /// </summary>
+ internal class EtwSession
+ {
+ public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
+ {
+ if (etwSessionId < 0)
+ return null;
+
+ EtwSession etwSession;
+ foreach (var wrEtwSession in s_etwSessions)
+ {
+#if ES_BUILD_STANDALONE
+ if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
+ return etwSession;
+#else
+ if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
+ return etwSession;
+#endif
+ }
+
+ if (!bCreateIfNeeded)
+ return null;
+
+#if ES_BUILD_STANDALONE
+ if (s_etwSessions == null)
+ s_etwSessions = new List<WeakReference>();
+
+ etwSession = new EtwSession(etwSessionId);
+ s_etwSessions.Add(new WeakReference(etwSession));
+#else
+ if (s_etwSessions == null)
+ s_etwSessions = new List<WeakReference<EtwSession>>();
+
+ etwSession = new EtwSession(etwSessionId);
+ s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
+#endif
+
+ if (s_etwSessions.Count > s_thrSessionCount)
+ TrimGlobalList();
+
+ return etwSession;
+
+ }
+
+ public static void RemoveEtwSession(EtwSession etwSession)
+ {
+ Contract.Assert(etwSession != null);
+ if (s_etwSessions == null || etwSession == null)
+ return;
+
+ s_etwSessions.RemoveAll((wrEtwSession) =>
+ {
+ EtwSession session;
+#if ES_BUILD_STANDALONE
+ return (session = (EtwSession) wrEtwSession.Target) != null &&
+ (session.m_etwSessionId == etwSession.m_etwSessionId);
+#else
+ return wrEtwSession.TryGetTarget(out session) &&
+ (session.m_etwSessionId == etwSession.m_etwSessionId);
+#endif
+ });
+
+ if (s_etwSessions.Count > s_thrSessionCount)
+ TrimGlobalList();
+ }
+
+ private static void TrimGlobalList()
+ {
+ if (s_etwSessions == null)
+ return;
+
+ s_etwSessions.RemoveAll((wrEtwSession) =>
+ {
+#if ES_BUILD_STANDALONE
+ return wrEtwSession.Target == null;
+#else
+ EtwSession session;
+ return !wrEtwSession.TryGetTarget(out session);
+#endif
+ });
+ }
+
+ private EtwSession(int etwSessionId)
+ {
+ m_etwSessionId = etwSessionId;
+ }
+
+ public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
+ public ActivityFilter m_activityFilter; // all filters enabled for this session
+
+#if ES_BUILD_STANDALONE
+ private static List<WeakReference> s_etwSessions = new List<WeakReference>();
+#else
+ private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
+#endif
+ private const int s_thrSessionCount = 16;
+ }
+
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // holds a bitfield representing a session mask
+ /// <summary>
+ /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
+ /// is the index in the SessionMask of the bit that will be set. These can translate to
+ /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
+ /// FromEventKeywords() methods.
+ /// </summary>
+ internal struct SessionMask
+ {
+ public SessionMask(SessionMask m)
+ { m_mask = m.m_mask; }
+
+ public SessionMask(uint mask = 0)
+ { m_mask = mask & MASK; }
+
+ public bool IsEqualOrSupersetOf(SessionMask m)
+ {
+ return (this.m_mask | m.m_mask) == this.m_mask;
+ }
+
+ public static SessionMask All
+ {
+ get { return new SessionMask(MASK); }
+ }
+
+ public static SessionMask FromId(int perEventSourceSessionId)
+ {
+ Contract.Assert(perEventSourceSessionId < MAX);
+ return new SessionMask((uint)1 << perEventSourceSessionId);
+ }
+
+ public ulong ToEventKeywords()
+ {
+ return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
+ }
+
+ public static SessionMask FromEventKeywords(ulong m)
+ {
+ return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
+ }
+
+ public bool this[int perEventSourceSessionId]
+ {
+ get
+ {
+ Contract.Assert(perEventSourceSessionId < MAX);
+ return (m_mask & (1 << perEventSourceSessionId)) != 0;
+ }
+ set
+ {
+ Contract.Assert(perEventSourceSessionId < MAX);
+ if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
+ else m_mask &= ~((uint)1 << perEventSourceSessionId);
+ }
+ }
+
+ public static SessionMask operator |(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask | m2.m_mask);
+ }
+
+ public static SessionMask operator &(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask & m2.m_mask);
+ }
+
+ public static SessionMask operator ^(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask ^ m2.m_mask);
+ }
+
+ public static SessionMask operator ~(SessionMask m)
+ {
+ return new SessionMask(MASK & ~(m.m_mask));
+ }
+
+ public static explicit operator ulong(SessionMask m)
+ { return m.m_mask; }
+
+ public static explicit operator uint(SessionMask m)
+ { return m.m_mask; }
+
+ private uint m_mask;
+
+ internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
+ internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
+ internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
+ }
+
+ /// <summary>
+ /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
+ /// (m_EventEnabled) for a particular EventSource X EventListener tuple
+ ///
+ /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
+ /// that that EventListener has activate) and a Single EventSource may also have many
+ /// event Dispatchers (one for every EventListener that has activated it).
+ ///
+ /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
+ /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
+ /// associated with.
+ /// </summary>
+ internal class EventDispatcher
+ {
+ internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
+ {
+ m_Next = next;
+ m_EventEnabled = eventEnabled;
+ m_Listener = listener;
+ }
+
+ // Instance fields
+ readonly internal EventListener m_Listener; // The dispatcher this entry is for
+ internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
+#if FEATURE_ACTIVITYSAMPLING
+ internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // Only guaranteed to exist after a InsureInit()
+ internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
+ // Of all listeners for that eventSource.
+ }
+
+ /// <summary>
+ /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
+ /// generated.
+ /// </summary>
+ [Flags]
+ public enum EventManifestOptions
+ {
+ /// <summary>
+ /// Only the resources associated with current UI culture are included in the manifest
+ /// </summary>
+ None = 0x0,
+ /// <summary>
+ /// Throw exceptions for any inconsistency encountered
+ /// </summary>
+ Strict = 0x1,
+ /// <summary>
+ /// Generate a "resources" node under "localization" for every satellite assembly provided
+ /// </summary>
+ AllCultures = 0x2,
+ /// <summary>
+ /// Generate the manifest only if the event source needs to be registered on the machine,
+ /// otherwise return null (but still perform validation if Strict is specified)
+ /// </summary>
+ OnlyIfNeededForRegistration = 0x4,
+ /// <summary>
+ /// When generating the manifest do *not* enforce the rule that the current EventSource class
+ /// must be the base class for the user-defined type passed in. This allows validation of .net
+ /// event sources using the new validation code
+ /// </summary>
+ AllowEventSourceOverride = 0x8,
+ }
+
+ /// <summary>
+ /// ManifestBuilder is designed to isolate the details of the message of the event from the
+ /// rest of EventSource. This one happens to create XML.
+ /// </summary>
+ internal partial class ManifestBuilder
+ {
+ /// <summary>
+ /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
+ /// 'resources, is a resource manager. If specified all messages are localized using that manager.
+ /// </summary>
+ public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
+ EventManifestOptions flags)
+ {
+#if FEATURE_MANAGED_ETW_CHANNELS
+ this.providerName = providerName;
+#endif
+ this.flags = flags;
+
+ this.resources = resources;
+ sb = new StringBuilder();
+ events = new StringBuilder();
+ templates = new StringBuilder();
+ opcodeTab = new Dictionary<int, string>();
+ stringTab = new Dictionary<string, string>();
+ errors = new List<string>();
+ perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
+
+ sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
+ sb.AppendLine(" <instrumentation xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\">");
+ sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
+ sb.Append("<provider name=\"").Append(providerName).
+ Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
+ if (dllName != null)
+ sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
+
+ var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
+ sb.Append("\" symbol=\"").Append(symbolsName);
+ sb.Append("\">").AppendLine();
+ }
+
+ public void AddOpcode(string name, int value)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value <= 10 || value >= 239)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
+ }
+ string prevName;
+ if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
+ }
+ }
+ opcodeTab[value] = name;
+ }
+ public void AddTask(string name, int value)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value <= 0 || value >= 65535)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalTaskValue", name, value));
+ }
+ string prevName;
+ if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_TaskCollision", name, prevName, value));
+ }
+ }
+ if (taskTab == null)
+ taskTab = new Dictionary<int, string>();
+ taskTab[value] = name;
+ }
+ public void AddKeyword(string name, ulong value)
+ {
+ if ((value & (value - 1)) != 0) // Is it a power of 2?
+ {
+ ManifestError(Resources.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
+ }
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
+ }
+ string prevName;
+ if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
+ }
+ }
+ if (keywordTab == null)
+ keywordTab = new Dictionary<ulong, string>();
+ keywordTab[value] = name;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Add a channel. channelAttribute can be null
+ /// </summary>
+ public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
+ {
+ EventChannel chValue = (EventChannel)value;
+ if (value < (int)EventChannel.Admin || value > 255)
+ ManifestError(Resources.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
+ else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
+ channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
+ {
+ // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
+ // but we want to allow them to override the default ones...
+ ManifestError(Resources.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
+ name, ((EventChannel)value).ToString()));
+ }
+
+ // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
+
+ ulong kwd = GetChannelKeyword(chValue);
+
+ if (channelTab == null)
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+ channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
+ }
+
+ private EventChannelType EventChannelToChannelType(EventChannel channel)
+ {
+#if !ES_BUILD_STANDALONE
+ Contract.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
+#endif
+ return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
+ }
+ private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
+ {
+ EventChannelAttribute attrib = new EventChannelAttribute();
+ attrib.EventChannelType = EventChannelToChannelType(channel);
+ if (attrib.EventChannelType <= EventChannelType.Operational)
+ attrib.Enabled = true;
+ return attrib;
+ }
+
+ public ulong[] GetChannelData()
+ {
+ if (this.channelTab == null)
+ {
+ return new ulong[0];
+ }
+
+ // We create an array indexed by the channel id for fast look up.
+ // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
+ int maxkey = -1;
+ foreach (var item in this.channelTab.Keys)
+ {
+ if (item > maxkey)
+ {
+ maxkey = item;
+ }
+ }
+
+ ulong[] channelMask = new ulong[maxkey + 1];
+ foreach (var item in this.channelTab)
+ {
+ channelMask[item.Key] = item.Value.Keywords;
+ }
+
+ return channelMask;
+ }
+
+#endif
+ public void StartEvent(string eventName, EventAttribute eventAttribute)
+ {
+ Contract.Assert(numParams == 0);
+ Contract.Assert(this.eventName == null);
+ this.eventName = eventName;
+ numParams = 0;
+ byteArrArgIndices = null;
+
+ events.Append(" <event").
+ Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
+ Append(" version=\"").Append(eventAttribute.Version).Append("\"").
+ Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
+ Append(" symbol=\"").Append(eventName).Append("\"");
+
+ // at this point we add to the manifest's stringTab a message that is as-of-yet
+ // "untranslated to manifest convention", b/c we don't have the number or position
+ // of any byte[] args (which require string format index updates)
+ WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
+
+ if (eventAttribute.Keywords != 0)
+ events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
+ if (eventAttribute.Opcode != 0)
+ events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
+ if (eventAttribute.Task != 0)
+ events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
+#if FEATURE_MANAGED_ETW_CHANNELS
+ if (eventAttribute.Channel != 0)
+ {
+ events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
+ }
+#endif
+ }
+
+ public void AddEventParameter(Type type, string name)
+ {
+ if (numParams == 0)
+ templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
+ if (type == typeof(byte[]))
+ {
+ // mark this index as "extraneous" (it has no parallel in the managed signature)
+ // we use these values in TranslateToManifestConvention()
+ if (byteArrArgIndices == null)
+ byteArrArgIndices = new List<int>(4);
+ byteArrArgIndices.Add(numParams);
+
+ // add an extra field to the template representing the length of the binary blob
+ numParams++;
+ templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
+ }
+ numParams++;
+ templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
+ // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
+ // as for 'byte[]' args (blob_arg_name + "Size")
+ if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
+ {
+ // add "length" attribute to the "blob" field in the template (referencing the field added above)
+ templates.Append(" length=\"").Append(name).Append("Size\"");
+ }
+ // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
+ if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
+ {
+ templates.Append(" map=\"").Append(type.Name).Append("\"");
+ if (mapsTab == null)
+ mapsTab = new Dictionary<string, Type>();
+ if (!mapsTab.ContainsKey(type.Name))
+ mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
+ }
+
+ templates.Append("/>").AppendLine();
+ }
+ public void EndEvent()
+ {
+ if (numParams > 0)
+ {
+ templates.Append(" </template>").AppendLine();
+ events.Append(" template=\"").Append(eventName).Append("Args\"");
+ }
+ events.Append("/>").AppendLine();
+
+ if (byteArrArgIndices != null)
+ perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
+
+ // at this point we have all the information we need to translate the C# Message
+ // to the manifest string we'll put in the stringTab
+ string msg;
+ if (stringTab.TryGetValue("event_" + eventName, out msg))
+ {
+ msg = TranslateToManifestConvention(msg, eventName);
+ stringTab["event_" + eventName] = msg;
+ }
+
+ eventName = null;
+ numParams = 0;
+ byteArrArgIndices = null;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
+ // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
+ // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
+ // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
+ // to channels by the OS infrastructure).
+ // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
+ // assumed that that the keyword for that channel should be that bit.
+ // otherwise we allocate a channel bit for the channel.
+ // explicit channel bits are only used by WCF to mimic an existing manifest,
+ // so we don't dont do error checking.
+ public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword=0)
+ {
+ // strip off any non-channel keywords, since we are only interested in channels here.
+ channelKeyword &= ValidPredefinedChannelKeywords;
+ if (channelTab == null)
+ {
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+ }
+
+ if (channelTab.Count == MaxCountChannels)
+ ManifestError(Resources.GetResourceString("EventSource_MaxChannelExceeded"));
+
+ ChannelInfo info;
+ if (!channelTab.TryGetValue((int)channel, out info))
+ {
+ // If we were not given an explicit channel, allocate one.
+ if (channelKeyword != 0)
+ {
+ channelKeyword = nextChannelKeywordBit;
+ nextChannelKeywordBit >>= 1;
+ }
+ }
+ else
+ {
+ channelKeyword = info.Keywords;
+ }
+
+ return channelKeyword;
+ }
+#endif
+
+ public byte[] CreateManifest()
+ {
+ string str = CreateManifestString();
+ return Encoding.UTF8.GetBytes(str);
+ }
+
+ public IList<string> Errors { get { return errors; } }
+
+ /// <summary>
+ /// When validating an event source it adds the error to the error collection.
+ /// When not validating it throws an exception if runtimeCritical is "true".
+ /// Otherwise the error is ignored.
+ /// </summary>
+ /// <param name="msg"></param>
+ /// <param name="runtimeCritical"></param>
+ public void ManifestError(string msg, bool runtimeCritical = false)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ errors.Add(msg);
+ else if (runtimeCritical)
+ throw new ArgumentException(msg);
+ }
+
+ private string CreateManifestString()
+ {
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // Write out the channels
+ if (channelTab != null)
+ {
+ sb.Append(" <channels>").AppendLine();
+ var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
+ foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
+ sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
+ foreach (var kvpair in sortedChannels)
+ {
+ int channel = kvpair.Key;
+ ChannelInfo channelInfo = kvpair.Value;
+
+ string channelType = null;
+ string elementName = "channel";
+ bool enabled = false;
+ string fullName = null;
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ string isolation = null;
+ string access = null;
+#endif
+ if (channelInfo.Attribs != null)
+ {
+ var attribs = channelInfo.Attribs;
+ if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
+ channelType = attribs.EventChannelType.ToString();
+ enabled = attribs.Enabled;
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ if (attribs.ImportChannel != null)
+ {
+ fullName = attribs.ImportChannel;
+ elementName = "importChannel";
+ }
+ if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
+ isolation = attribs.Isolation.ToString();
+ access = attribs.Access;
+#endif
+ }
+ if (fullName == null)
+ fullName = providerName + "/" + channelInfo.Name;
+
+ sb.Append(" <").Append(elementName);
+ sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
+ sb.Append(" name=\"").Append(fullName).Append("\"");
+ if (elementName == "channel") // not applicable to importChannels.
+ {
+ WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
+ sb.Append(" value=\"").Append(channel).Append("\"");
+ if (channelType != null)
+ sb.Append(" type=\"").Append(channelType).Append("\"");
+ sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ if (access != null)
+ sb.Append(" access=\"").Append(access).Append("\"");
+ if (isolation != null)
+ sb.Append(" isolation=\"").Append(isolation).Append("\"");
+#endif
+ }
+ sb.Append("/>").AppendLine();
+ }
+ sb.Append(" </channels>").AppendLine();
+ }
+#endif
+
+ // Write out the tasks
+ if (taskTab != null)
+ {
+
+ sb.Append(" <tasks>").AppendLine();
+ var sortedTasks = new List<int>(taskTab.Keys);
+ sortedTasks.Sort();
+ foreach (int task in sortedTasks)
+ {
+ sb.Append(" <task");
+ WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
+ sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </tasks>").AppendLine();
+ }
+
+ // Write out the maps
+ if (mapsTab != null)
+ {
+ sb.Append(" <maps>").AppendLine();
+ foreach (Type enumType in mapsTab.Values)
+ {
+ bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
+ string mapKind = isbitmap ? "bitMap" : "valueMap";
+ sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
+
+ // write out each enum value
+ FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
+ foreach (FieldInfo staticField in staticFields)
+ {
+ object constantValObj = staticField.GetRawConstantValue();
+ if (constantValObj != null)
+ {
+ long hexValue;
+ if (constantValObj is int)
+ hexValue = ((int)constantValObj);
+ else if (constantValObj is long)
+ hexValue = ((long)constantValObj);
+ else
+ continue;
+
+ // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
+ // TODO: Warn people about the dropping of values.
+ if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
+ continue;
+
+ sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
+ WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
+ sb.Append("/>").AppendLine();
+ }
+ }
+ sb.Append(" </").Append(mapKind).Append(">").AppendLine();
+ }
+ sb.Append(" </maps>").AppendLine();
+ }
+
+ // Write out the opcodes
+ sb.Append(" <opcodes>").AppendLine();
+ var sortedOpcodes = new List<int>(opcodeTab.Keys);
+ sortedOpcodes.Sort();
+ foreach (int opcode in sortedOpcodes)
+ {
+ sb.Append(" <opcode");
+ WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
+ sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </opcodes>").AppendLine();
+
+ // Write out the keywords
+ if (keywordTab != null)
+ {
+ sb.Append(" <keywords>").AppendLine();
+ var sortedKeywords = new List<ulong>(keywordTab.Keys);
+ sortedKeywords.Sort();
+ foreach (ulong keyword in sortedKeywords)
+ {
+ sb.Append(" <keyword");
+ WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
+ sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </keywords>").AppendLine();
+ }
+
+ sb.Append(" <events>").AppendLine();
+ sb.Append(events);
+ sb.Append(" </events>").AppendLine();
+
+ sb.Append(" <templates>").AppendLine();
+ if (templates.Length > 0)
+ {
+ sb.Append(templates);
+ }
+ else
+ {
+ // Work around a cornercase ETW issue where a manifest with no templates causes
+ // ETW events to not get sent to their associated channel.
+ sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
+ }
+ sb.Append(" </templates>").AppendLine();
+
+ sb.Append("</provider>").AppendLine();
+ sb.Append("</events>").AppendLine();
+ sb.Append("</instrumentation>").AppendLine();
+
+ // Output the localization information.
+ sb.Append("<localization>").AppendLine();
+
+ List<CultureInfo> cultures = null;
+ if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
+ {
+ cultures = GetSupportedCultures(resources);
+ }
+ else
+ {
+ cultures = new List<CultureInfo>();
+ cultures.Add(CultureInfo.CurrentUICulture);
+ }
+#if ES_BUILD_STANDALONE || PROJECTN
+ var sortedStrings = new List<string>(stringTab.Keys);
+ sortedStrings.Sort();
+#else
+ // DD 947936
+ var sortedStrings = new string[stringTab.Keys.Count];
+ stringTab.Keys.CopyTo(sortedStrings, 0);
+ // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
+ // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
+ // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
+ // access BinaryCompatibility.
+ ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer<string>.Default);
+#endif
+ foreach (var ci in cultures)
+ {
+ sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
+ sb.Append(" <stringTable>").AppendLine();
+
+ foreach (var stringKey in sortedStrings)
+ {
+ string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
+ sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </stringTable>").AppendLine();
+ sb.Append(" </resources>").AppendLine();
+ }
+ sb.Append("</localization>").AppendLine();
+ sb.AppendLine("</instrumentationManifest>");
+ return sb.ToString();
+ }
+
+ #region private
+ private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
+ {
+ stringBuilder.Append(" name=\"").Append(name).Append("\"");
+ WriteMessageAttrib(sb, elementName, name, name);
+ }
+ private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
+ {
+ string key = elementName + "_" + name;
+ // See if the user wants things localized.
+ if (resources != null)
+ {
+ // resource fallback: strings in the neutral culture will take precedence over inline strings
+ string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
+ if (localizedString != null)
+ value = localizedString;
+ }
+ if (value == null)
+ return;
+
+ stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
+ string prevValue;
+ if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_DuplicateStringKey", key), true);
+ return;
+ }
+
+ stringTab[key] = value;
+ }
+ internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
+ {
+ string value = null;
+ if (resources != null)
+ {
+ string localizedString = resources.GetString(key, ci);
+ if (localizedString != null)
+ {
+ value = localizedString;
+ if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
+ {
+ var evtName = key.Substring("event_".Length);
+ value = TranslateToManifestConvention(value, evtName);
+ }
+ }
+ }
+ if (etwFormat && value == null)
+ stringTab.TryGetValue(key, out value);
+
+ return value;
+ }
+
+ /// <summary>
+ /// There's no API to enumerate all languages an assembly is localized into, so instead
+ /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
+ /// assembly
+ /// </summary>
+ /// <param name="resources"></param>
+ /// <returns></returns>
+ private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
+ {
+ var cultures = new List<CultureInfo>();
+#if !ES_BUILD_PCL && !FEATURE_CORECLR && !PROJECTN
+ foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures /*| CultureTypes.NeutralCultures*/))
+ {
+ if (resources.GetResourceSet(ci, true, false) != null)
+ cultures.Add(ci);
+ }
+#endif // !ES_BUILD_PCL && !FEATURE_CORECLR
+ if (!cultures.Contains(CultureInfo.CurrentUICulture))
+ cultures.Insert(0, CultureInfo.CurrentUICulture);
+ return cultures;
+ }
+
+ private static string GetLevelName(EventLevel level)
+ {
+ return (((int)level >= 16) ? "" : "win:") + level.ToString();
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
+ {
+ ChannelInfo info = null;
+ if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
+ {
+ if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
+
+ // allow channels to be auto-defined. The well known ones get their well known names, and the
+ // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
+ if (channelTab == null)
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+
+ string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
+ if (EventChannel.Debug < channel)
+ channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
+
+ AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
+ if (!channelTab.TryGetValue((int)channel, out info))
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
+ }
+ // events that specify admin channels *must* have non-null "Message" attributes
+ if (resources != null && eventMessage == null)
+ eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
+ if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
+ ManifestError(Resources.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
+ return info.Name;
+ }
+#endif
+ private string GetTaskName(EventTask task, string eventName)
+ {
+ if (task == EventTask.None)
+ return "";
+
+ string ret;
+ if (taskTab == null)
+ taskTab = new Dictionary<int, string>();
+ if (!taskTab.TryGetValue((int)task, out ret))
+ ret = taskTab[(int)task] = eventName;
+ return ret;
+ }
+
+ private string GetOpcodeName(EventOpcode opcode, string eventName)
+ {
+ switch (opcode)
+ {
+ case EventOpcode.Info:
+ return "win:Info";
+ case EventOpcode.Start:
+ return "win:Start";
+ case EventOpcode.Stop:
+ return "win:Stop";
+ case EventOpcode.DataCollectionStart:
+ return "win:DC_Start";
+ case EventOpcode.DataCollectionStop:
+ return "win:DC_Stop";
+ case EventOpcode.Extension:
+ return "win:Extension";
+ case EventOpcode.Reply:
+ return "win:Reply";
+ case EventOpcode.Resume:
+ return "win:Resume";
+ case EventOpcode.Suspend:
+ return "win:Suspend";
+ case EventOpcode.Send:
+ return "win:Send";
+ case EventOpcode.Receive:
+ return "win:Receive";
+ }
+
+ string ret;
+ if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
+ ret = null;
+ }
+ return ret;
+ }
+
+ private string GetKeywords(ulong keywords, string eventName)
+ {
+ // ignore keywords associate with channels
+ // See ValidPredefinedChannelKeywords def for more.
+ keywords &= ~ValidPredefinedChannelKeywords;
+
+ string ret = "";
+ for (ulong bit = 1; bit != 0; bit <<= 1)
+ {
+ if ((keywords & bit) != 0)
+ {
+ string keyword = null;
+ if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
+ (bit >= (ulong)0x1000000000000))
+ {
+ // do not report Windows reserved keywords in the manifest (this allows the code
+ // to be resilient to potential renaming of these keywords)
+ keyword = string.Empty;
+ }
+ if (keyword == null)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
+ keyword = string.Empty;
+ }
+ if (ret.Length != 0 && keyword.Length != 0)
+ ret = ret + " ";
+ ret = ret + keyword;
+ }
+ }
+ return ret;
+ }
+
+ private string GetTypeName(Type type)
+ {
+ if (type.IsEnum())
+ {
+ FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ var typeName = GetTypeName(fields[0].FieldType);
+ return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
+ }
+
+ return GetTypeNameHelper(type);
+ }
+
+ private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
+ {
+ if (stringBuilder == null)
+ stringBuilder = new StringBuilder();
+ stringBuilder.Append(eventMessage, startIndex, count);
+ }
+
+ // Manifest messages use %N conventions for their message substitutions. Translate from
+ // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
+ private string TranslateToManifestConvention(string eventMessage, string evtName)
+ {
+ StringBuilder stringBuilder = null; // We lazily create this
+ int writtenSoFar = 0;
+ int chIdx = -1;
+ for (int i = 0; ; )
+ {
+ if (i >= eventMessage.Length)
+ {
+ if (stringBuilder == null)
+ return eventMessage;
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ return stringBuilder.ToString();
+ }
+
+ if (eventMessage[i] == '%')
+ {
+ // handle format message escaping character '%' by escaping it
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ stringBuilder.Append("%%");
+ i++;
+ writtenSoFar = i;
+ }
+ else if (i < eventMessage.Length - 1 &&
+ (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
+ {
+ // handle C# escaped '{" and '}'
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ stringBuilder.Append(eventMessage[i]);
+ i++; i++;
+ writtenSoFar = i;
+ }
+ else if (eventMessage[i] == '{')
+ {
+ int leftBracket = i;
+ i++;
+ int argNum = 0;
+ while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
+ {
+ argNum = argNum * 10 + eventMessage[i] - '0';
+ i++;
+ }
+ if (i < eventMessage.Length && eventMessage[i] == '}')
+ {
+ i++;
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
+ int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
+ stringBuilder.Append('%').Append(manIndex);
+ // An '!' after the insert specifier {n} will be interpreted as a literal.
+ // We'll escape it so that mc.exe does not attempt to consider it the
+ // beginning of a format string.
+ if (i < eventMessage.Length && eventMessage[i] == '!')
+ {
+ i++;
+ stringBuilder.Append("%!");
+ }
+ writtenSoFar = i;
+ }
+ else
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
+ }
+ }
+ else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
+ {
+ string[] escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
+ var update = new Action<char, string>(
+ (ch, escape) =>
+ {
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ i++;
+ stringBuilder.Append(escape);
+ writtenSoFar = i;
+ });
+ update(eventMessage[i], escapes[chIdx]);
+ }
+ else
+ i++;
+ }
+ }
+
+ private int TranslateIndexToManifestConvention(int idx, string evtName)
+ {
+ List<int> byteArrArgIndices;
+ if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
+ {
+ foreach (var byArrIdx in byteArrArgIndices)
+ {
+ if (idx >= byArrIdx)
+ ++idx;
+ else
+ break;
+ }
+ }
+ return idx + 1;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ class ChannelInfo
+ {
+ public string Name;
+ public ulong Keywords;
+ public EventChannelAttribute Attribs;
+ }
+#endif
+
+ Dictionary<int, string> opcodeTab;
+ Dictionary<int, string> taskTab;
+#if FEATURE_MANAGED_ETW_CHANNELS
+ Dictionary<int, ChannelInfo> channelTab;
+#endif
+ Dictionary<ulong, string> keywordTab;
+ Dictionary<string, Type> mapsTab;
+
+ Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // WCF used EventSource to mimic a existing ETW manifest. To support this
+ // in just their case, we allowed them to specify the keywords associated
+ // with their channels explicitly. ValidPredefinedChannelKeywords is
+ // this set of channel keywords that we allow to be explicitly set. You
+ // can ignore these bits otherwise.
+ internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
+ ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
+ const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
+#endif
+
+ StringBuilder sb; // Holds the provider information.
+ StringBuilder events; // Holds the events.
+ StringBuilder templates;
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ string providerName;
+#endif
+ ResourceManager resources; // Look up localized strings here.
+ EventManifestOptions flags;
+ IList<string> errors; // list of currently encountered errors
+ Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
+
+ // State we track between StartEvent and EndEvent.
+ string eventName; // Name of the event currently being processed.
+ int numParams; // keeps track of the number of args the event has.
+ List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
+ #endregion
+ }
+
+ /// <summary>
+ /// Used to send the m_rawManifest into the event dispatcher as a series of events.
+ /// </summary>
+ internal struct ManifestEnvelope
+ {
+ public const int MaxChunkSize = 0xFF00;
+ public enum ManifestFormats : byte
+ {
+ SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
+ }
+
+ public ManifestFormats Format;
+ public byte MajorVersion;
+ public byte MinorVersion;
+ public byte Magic;
+ public ushort TotalChunks;
+ public ushort ChunkNumber;
+ };
+
+ #endregion
+}
+
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSourceException.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSourceException.cs
new file mode 100644
index 0000000000..3fc9d545b8
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSourceException.cs
@@ -0,0 +1,53 @@
+// 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.Serialization;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Exception that is thrown when an error occurs during EventSource operation.
+ /// </summary>
+#if (!ES_BUILD_PCL && !PROJECTN)
+ [Serializable]
+#endif
+ public class EventSourceException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class.
+ /// </summary>
+ public EventSourceException() :
+ base(Resources.GetResourceString("EventSource_ListenerWriteFailure")) { }
+
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with a specified error message.
+ /// </summary>
+ public EventSourceException(string message) : base(message) { }
+
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with a specified error message
+ /// and a reference to the inner exception that is the cause of this exception.
+ /// </summary>
+ public EventSourceException(string message, Exception innerException) : base(message, innerException) { }
+
+#if (!ES_BUILD_PCL && !PROJECTN)
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with serialized data.
+ /// </summary>
+ protected EventSourceException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+#endif
+
+ internal EventSourceException(Exception innerException) :
+ base(Resources.GetResourceString("EventSource_ListenerWriteFailure"), innerException) { }
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
new file mode 100644
index 0000000000..00bd0b7caa
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
@@ -0,0 +1,224 @@
+// 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.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.Reflection;
+using Microsoft.Win32;
+
+namespace System.Diagnostics.Tracing
+{
+ public partial class EventSource
+ {
+ // ActivityID support (see also WriteEventWithRelatedActivityIdCore)
+ /// <summary>
+ /// When a thread starts work that is on behalf of 'something else' (typically another
+ /// thread or network request) it should mark the thread as working on that other work.
+ /// This API marks the current thread as working on activity 'activityID'. This API
+ /// should be used when the caller knows the thread's current activity (the one being
+ /// overwritten) has completed. Otherwise, callers should prefer the overload that
+ /// return the oldActivityThatWillContinue (below).
+ ///
+ /// All events created with the EventSource on this thread are also tagged with the
+ /// activity ID of the thread.
+ ///
+ /// It is common, and good practice after setting the thread to an activity to log an event
+ /// with a 'start' opcode to indicate that precise time/thread where the new activity
+ /// started.
+ /// </summary>
+ /// <param name="activityId">A Guid that represents the new activity with which to mark
+ /// the current thread</param>
+ [System.Security.SecuritySafeCritical]
+ public static void SetCurrentThreadActivityId(Guid activityId)
+ {
+ if (TplEtwProvider.Log != null)
+ TplEtwProvider.Log.SetActivityId(activityId);
+#if FEATURE_MANAGED_ETW
+#if FEATURE_ACTIVITYSAMPLING
+ Guid newId = activityId;
+#endif // FEATURE_ACTIVITYSAMPLING
+ // We ignore errors to keep with the convention that EventSources do not throw errors.
+ // Note we can't access m_throwOnWrites because this is a static method.
+
+ if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
+ UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref activityId) == 0)
+ {
+#if FEATURE_ACTIVITYSAMPLING
+ var activityDying = s_activityDying;
+ if (activityDying != null && newId != activityId)
+ {
+ if (activityId == Guid.Empty)
+ {
+ activityId = FallbackActivityId;
+ }
+ // OutputDebugString(string.Format("Activity dying: {0} -> {1}", activityId, newId));
+ activityDying(activityId); // This is actually the OLD activity ID.
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ /// <summary>
+ /// When a thread starts work that is on behalf of 'something else' (typically another
+ /// thread or network request) it should mark the thread as working on that other work.
+ /// This API marks the current thread as working on activity 'activityID'. It returns
+ /// whatever activity the thread was previously marked with. There is a convention that
+ /// callers can assume that callees restore this activity mark before the callee returns.
+ /// To encourage this this API returns the old activity, so that it can be restored later.
+ ///
+ /// All events created with the EventSource on this thread are also tagged with the
+ /// activity ID of the thread.
+ ///
+ /// It is common, and good practice after setting the thread to an activity to log an event
+ /// with a 'start' opcode to indicate that precise time/thread where the new activity
+ /// started.
+ /// </summary>
+ /// <param name="activityId">A Guid that represents the new activity with which to mark
+ /// the current thread</param>
+ /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
+ /// which will continue at some point in the future, on the current thread</param>
+ [System.Security.SecuritySafeCritical]
+ public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
+ {
+ oldActivityThatWillContinue = activityId;
+#if FEATURE_MANAGED_ETW
+ // We ignore errors to keep with the convention that EventSources do not throw errors.
+ // Note we can't access m_throwOnWrites because this is a static method.
+
+ UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
+ UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref oldActivityThatWillContinue);
+#endif // FEATURE_MANAGED_ETW
+
+ // We don't call the activityDying callback here because the caller has declared that
+ // it is not dying.
+ if (TplEtwProvider.Log != null)
+ TplEtwProvider.Log.SetActivityId(activityId);
+ }
+
+ /// <summary>
+ /// Retrieves the ETW activity ID associated with the current thread.
+ /// </summary>
+ public static Guid CurrentThreadActivityId
+ {
+ [System.Security.SecuritySafeCritical]
+ get
+ {
+ // We ignore errors to keep with the convention that EventSources do not throw
+ // errors. Note we can't access m_throwOnWrites because this is a static method.
+ Guid retVal = new Guid();
+#if FEATURE_MANAGED_ETW
+ UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
+ UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref retVal);
+#endif // FEATURE_MANAGED_ETW
+ return retVal;
+ }
+ }
+
+ private int GetParameterCount(EventMetadata eventData)
+ {
+ return eventData.Parameters.Length;
+ }
+
+ private Type GetDataType(EventMetadata eventData, int parameterId)
+ {
+ return eventData.Parameters[parameterId].ParameterType;
+ }
+
+ private static string GetResourceString(string key, params object[] args)
+ {
+ return Environment.GetResourceString(key, args);
+ }
+
+ private static readonly bool m_EventSourcePreventRecursion = false;
+ }
+
+ internal partial class ManifestBuilder
+ {
+ private string GetTypeNameHelper(Type type)
+ {
+ switch (type.GetTypeCode())
+ {
+ case TypeCode.Boolean:
+ return "win:Boolean";
+ case TypeCode.Byte:
+ return "win:UInt8";
+ case TypeCode.Char:
+ case TypeCode.UInt16:
+ return "win:UInt16";
+ case TypeCode.UInt32:
+ return "win:UInt32";
+ case TypeCode.UInt64:
+ return "win:UInt64";
+ case TypeCode.SByte:
+ return "win:Int8";
+ case TypeCode.Int16:
+ return "win:Int16";
+ case TypeCode.Int32:
+ return "win:Int32";
+ case TypeCode.Int64:
+ return "win:Int64";
+ case TypeCode.String:
+ return "win:UnicodeString";
+ case TypeCode.Single:
+ return "win:Float";
+ case TypeCode.Double:
+ return "win:Double";
+ case TypeCode.DateTime:
+ return "win:FILETIME";
+ default:
+ if (type == typeof(Guid))
+ return "win:GUID";
+ else if (type == typeof(IntPtr))
+ return "win:Pointer";
+ else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
+ return "win:Binary";
+
+ ManifestError(Resources.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true);
+ return string.Empty;
+ }
+ }
+ }
+
+ internal partial class EventProvider
+ {
+ [System.Security.SecurityCritical]
+ internal unsafe int SetInformation(
+ UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS eventInfoClass,
+ IntPtr data,
+ uint dataSize)
+ {
+ int status = UnsafeNativeMethods.ManifestEtw.ERROR_NOT_SUPPORTED;
+
+ if (!m_setInformationMissing)
+ {
+ try
+ {
+ status = UnsafeNativeMethods.ManifestEtw.EventSetInformation(
+ m_regHandle,
+ eventInfoClass,
+ (void *)data,
+ (int)dataSize);
+ }
+ catch (TypeLoadException)
+ {
+ m_setInformationMissing = true;
+ }
+ }
+
+ return status;
+ }
+ }
+
+ internal static class Resources
+ {
+ internal static string GetResourceString(string key, params object[] args)
+ {
+ return Environment.GetResourceString(key, args);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/FrameworkEventSource.cs b/src/mscorlib/src/System/Diagnostics/Eventing/FrameworkEventSource.cs
new file mode 100644
index 0000000000..6d3e28fcab
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/FrameworkEventSource.cs
@@ -0,0 +1,620 @@
+// 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.
+
+//
+// ResourcesEtwProvider.cs
+//
+//
+// Managed event source for things that can version with MSCORLIB.
+//
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using System.Text;
+using System.Runtime.CompilerServices;
+
+namespace System.Diagnostics.Tracing {
+
+ // To use the framework provider
+ //
+ // \\clrmain\tools\Perfmonitor /nokernel /noclr /provider:8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1 start
+ // Run run your app
+ // \\clrmain\tools\Perfmonitor stop
+ // \\clrmain\tools\Perfmonitor print
+ //
+ // This will produce an XML file, where each event is pretty-printed with all its arguments nicely parsed.
+ //
+ [FriendAccessAllowed]
+ [EventSource(Guid = "8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1", Name = "System.Diagnostics.Eventing.FrameworkEventSource")]
+ sealed internal class FrameworkEventSource : EventSource {
+ // Defines the singleton instance for the Resources ETW provider
+ public static readonly FrameworkEventSource Log = new FrameworkEventSource();
+
+ // Keyword definitions. These represent logical groups of events that can be turned on and off independently
+ // Often each task has a keyword, but where tasks are determined by subsystem, keywords are determined by
+ // usefulness to end users to filter. Generally users don't mind extra events if they are not high volume
+ // so grouping low volume events together in a single keywords is OK (users can post-filter by task if desired)
+ public static class Keywords {
+ public const EventKeywords Loader = (EventKeywords)0x0001; // This is bit 0
+ public const EventKeywords ThreadPool = (EventKeywords)0x0002;
+ public const EventKeywords NetClient = (EventKeywords)0x0004;
+ //
+ // This is a private event we do not want to expose to customers. It is to be used for profiling
+ // uses of dynamic type loading by ProjectN applications running on the desktop CLR
+ //
+ public const EventKeywords DynamicTypeUsage = (EventKeywords)0x0008;
+ public const EventKeywords ThreadTransfer = (EventKeywords)0x0010;
+ }
+
+ /// <summary>ETW tasks that have start/stop events.</summary>
+ [FriendAccessAllowed]
+ public static class Tasks // this name is important for EventSource
+ {
+ /// <summary>Begin / End - GetResponse.</summary>
+ public const EventTask GetResponse = (EventTask)1;
+ /// <summary>Begin / End - GetRequestStream</summary>
+ public const EventTask GetRequestStream = (EventTask)2;
+ /// <summary>Send / Receive - begin transfer/end transfer</summary>
+ public const EventTask ThreadTransfer = (EventTask)3;
+ }
+
+ [FriendAccessAllowed]
+ public static class Opcodes
+ {
+ public const EventOpcode ReceiveHandled = (EventOpcode)11;
+ }
+
+ // This predicate is used by consumers of this class to deteremine if the class has actually been initialized,
+ // and therefore if the public statics are available for use. This is typically not a problem... if the static
+ // class constructor fails, then attempts to access the statics (or even this property) will result in a
+ // TypeInitializationException. However, that is not the case while the class loader is actually trying to construct
+ // the TypeInitializationException instance to represent that failure, and some consumers of this class are on
+ // that code path, specifically the resource manager.
+ public static bool IsInitialized
+ {
+ get
+ {
+ return Log != null;
+ }
+ }
+
+ // The FrameworkEventSource GUID is {8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1}
+ private FrameworkEventSource() : base(new Guid(0x8e9f5090, 0x2d75, 0x4d03, 0x8a, 0x81, 0xe5, 0xaf, 0xbf, 0x85, 0xda, 0xf1), "System.Diagnostics.Eventing.FrameworkEventSource") { }
+
+ // WriteEvent overloads (to avoid the "params" EventSource.WriteEvent
+
+ // optimized for common signatures (used by the ThreadTransferSend/Receive events)
+ [NonEvent, System.Security.SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEvent(int eventId, long arg1, int arg2, string arg3, bool arg4)
+ {
+ if (IsEnabled())
+ {
+ if (arg3 == null) arg3 = "";
+ fixed (char* string3Bytes = arg3)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[4];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)string3Bytes;
+ descrs[2].Size = ((arg3.Length + 1) * 2);
+ descrs[3].DataPointer = (IntPtr)(&arg4);
+ descrs[3].Size = 4;
+ WriteEventCore(eventId, 4, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (used by the ThreadTransferSend/Receive events)
+ [NonEvent, System.Security.SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEvent(int eventId, long arg1, int arg2, string arg3)
+ {
+ if (IsEnabled())
+ {
+ if (arg3 == null) arg3 = "";
+ fixed (char* string3Bytes = arg3)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)string3Bytes;
+ descrs[2].Size = ((arg3.Length + 1) * 2);
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (used by the BeginGetResponse/BeginGetRequestStream events)
+ [NonEvent, System.Security.SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEvent(int eventId, long arg1, string arg2, bool arg3, bool arg4)
+ {
+ if (IsEnabled())
+ {
+ if (arg2 == null) arg2 = "";
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[4];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ descrs[3].DataPointer = (IntPtr)(&arg4);
+ descrs[3].Size = 4;
+ WriteEventCore(eventId, 4, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (used by the EndGetRequestStream event)
+ [NonEvent, System.Security.SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEvent(int eventId, long arg1, bool arg2, bool arg3)
+ {
+ if (IsEnabled())
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+
+ // optimized for common signatures (used by the EndGetResponse event)
+ [NonEvent, System.Security.SecuritySafeCritical]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEvent(int eventId, long arg1, bool arg2, bool arg3, int arg4)
+ {
+ if (IsEnabled())
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[4];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ descrs[3].DataPointer = (IntPtr)(&arg4);
+ descrs[3].Size = 4;
+ WriteEventCore(eventId, 4, descrs);
+ }
+ }
+
+ // ResourceManager Event Definitions
+
+ [Event(1, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerLookupStarted(String baseName, String mainAssemblyName, String cultureName) {
+ WriteEvent(1, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(2, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerLookingForResourceSet(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(2, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(3, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerFoundResourceSetInCache(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(3, baseName, mainAssemblyName, cultureName);
+ }
+
+ // After loading a satellite assembly, we already have the ResourceSet for this culture in
+ // the cache. This can happen if you have an assembly load callback that called into this
+ // instance of the ResourceManager.
+ [Event(4, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerFoundResourceSetInCacheUnexpected(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(4, baseName, mainAssemblyName, cultureName);
+ }
+
+ // manifest resource stream lookup succeeded
+ [Event(5, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerStreamFound(String baseName, String mainAssemblyName, String cultureName, String loadedAssemblyName, String resourceFileName) {
+ if (IsEnabled())
+ WriteEvent(5, baseName, mainAssemblyName, cultureName, loadedAssemblyName, resourceFileName);
+ }
+
+ // manifest resource stream lookup failed
+ [Event(6, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerStreamNotFound(String baseName, String mainAssemblyName, String cultureName, String loadedAssemblyName, String resourceFileName) {
+ if (IsEnabled())
+ WriteEvent(6, baseName, mainAssemblyName, cultureName, loadedAssemblyName, resourceFileName);
+ }
+
+ [Event(7, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerGetSatelliteAssemblySucceeded(String baseName, String mainAssemblyName, String cultureName, String assemblyName) {
+ if (IsEnabled())
+ WriteEvent(7, baseName, mainAssemblyName, cultureName, assemblyName);
+ }
+
+ [Event(8, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerGetSatelliteAssemblyFailed(String baseName, String mainAssemblyName, String cultureName, String assemblyName) {
+ if (IsEnabled())
+ WriteEvent(8, baseName, mainAssemblyName, cultureName, assemblyName);
+ }
+
+ [Event(9, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerCaseInsensitiveResourceStreamLookupSucceeded(String baseName, String mainAssemblyName, String assemblyName, String resourceFileName) {
+ if (IsEnabled())
+ WriteEvent(9, baseName, mainAssemblyName, assemblyName, resourceFileName);
+ }
+
+ [Event(10, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerCaseInsensitiveResourceStreamLookupFailed(String baseName, String mainAssemblyName, String assemblyName, String resourceFileName) {
+ if (IsEnabled())
+ WriteEvent(10, baseName, mainAssemblyName, assemblyName, resourceFileName);
+ }
+
+ // Could not access the manifest resource the assembly
+ [Event(11, Level = EventLevel.Error, Keywords = Keywords.Loader)]
+ public void ResourceManagerManifestResourceAccessDenied(String baseName, String mainAssemblyName, String assemblyName, String canonicalName) {
+ if (IsEnabled())
+ WriteEvent(11, baseName, mainAssemblyName, assemblyName, canonicalName);
+ }
+
+ // Neutral resources are sufficient for this culture. Skipping satellites
+ [Event(12, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerNeutralResourcesSufficient(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(12, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(13, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerNeutralResourceAttributeMissing(String mainAssemblyName) {
+ if (IsEnabled())
+ WriteEvent(13, mainAssemblyName);
+ }
+
+ [Event(14, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerCreatingResourceSet(String baseName, String mainAssemblyName, String cultureName, String fileName) {
+ if (IsEnabled())
+ WriteEvent(14, baseName, mainAssemblyName, cultureName, fileName);
+ }
+
+ [Event(15, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerNotCreatingResourceSet(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(15, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(16, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerLookupFailed(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(16, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(17, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerReleasingResources(String baseName, String mainAssemblyName) {
+ if (IsEnabled())
+ WriteEvent(17, baseName, mainAssemblyName);
+ }
+
+ [Event(18, Level = EventLevel.Warning, Keywords = Keywords.Loader)]
+ public void ResourceManagerNeutralResourcesNotFound(String baseName, String mainAssemblyName, String resName) {
+ if (IsEnabled())
+ WriteEvent(18, baseName, mainAssemblyName, resName);
+ }
+
+ [Event(19, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerNeutralResourcesFound(String baseName, String mainAssemblyName, String resName) {
+ if (IsEnabled())
+ WriteEvent(19, baseName, mainAssemblyName, resName);
+ }
+
+ [Event(20, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerAddingCultureFromConfigFile(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(20, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(21, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerCultureNotFoundInConfigFile(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(21, baseName, mainAssemblyName, cultureName);
+ }
+
+ [Event(22, Level = EventLevel.Informational, Keywords = Keywords.Loader)]
+ public void ResourceManagerCultureFoundInConfigFile(String baseName, String mainAssemblyName, String cultureName) {
+ if (IsEnabled())
+ WriteEvent(22, baseName, mainAssemblyName, cultureName);
+ }
+
+
+ // ResourceManager Event Wrappers
+
+ [NonEvent]
+ public void ResourceManagerLookupStarted(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerLookupStarted(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerLookingForResourceSet(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerLookingForResourceSet(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerFoundResourceSetInCache(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerFoundResourceSetInCache(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerFoundResourceSetInCacheUnexpected(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerFoundResourceSetInCacheUnexpected(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerStreamFound(String baseName, Assembly mainAssembly, String cultureName, Assembly loadedAssembly, String resourceFileName) {
+ if (IsEnabled())
+ ResourceManagerStreamFound(baseName, GetName(mainAssembly), cultureName, GetName(loadedAssembly), resourceFileName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerStreamNotFound(String baseName, Assembly mainAssembly, String cultureName, Assembly loadedAssembly, String resourceFileName) {
+ if (IsEnabled())
+ ResourceManagerStreamNotFound(baseName, GetName(mainAssembly), cultureName, GetName(loadedAssembly), resourceFileName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerGetSatelliteAssemblySucceeded(String baseName, Assembly mainAssembly, String cultureName, String assemblyName) {
+ if (IsEnabled())
+ ResourceManagerGetSatelliteAssemblySucceeded(baseName, GetName(mainAssembly), cultureName, assemblyName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerGetSatelliteAssemblyFailed(String baseName, Assembly mainAssembly, String cultureName, String assemblyName) {
+ if (IsEnabled())
+ ResourceManagerGetSatelliteAssemblyFailed(baseName, GetName(mainAssembly), cultureName, assemblyName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerCaseInsensitiveResourceStreamLookupSucceeded(String baseName, Assembly mainAssembly, String assemblyName, String resourceFileName) {
+ if (IsEnabled())
+ ResourceManagerCaseInsensitiveResourceStreamLookupSucceeded(baseName, GetName(mainAssembly), assemblyName, resourceFileName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerCaseInsensitiveResourceStreamLookupFailed(String baseName, Assembly mainAssembly, String assemblyName, String resourceFileName) {
+ if (IsEnabled())
+ ResourceManagerCaseInsensitiveResourceStreamLookupFailed(baseName, GetName(mainAssembly), assemblyName, resourceFileName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerManifestResourceAccessDenied(String baseName, Assembly mainAssembly, String assemblyName, String canonicalName) {
+ if (IsEnabled())
+ ResourceManagerManifestResourceAccessDenied(baseName, GetName(mainAssembly), assemblyName, canonicalName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerNeutralResourcesSufficient(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerNeutralResourcesSufficient(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerNeutralResourceAttributeMissing(Assembly mainAssembly) {
+ if (IsEnabled())
+ ResourceManagerNeutralResourceAttributeMissing(GetName(mainAssembly));
+ }
+
+ [NonEvent]
+ public void ResourceManagerCreatingResourceSet(String baseName, Assembly mainAssembly, String cultureName, String fileName) {
+ if (IsEnabled())
+ ResourceManagerCreatingResourceSet(baseName, GetName(mainAssembly), cultureName, fileName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerNotCreatingResourceSet(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerNotCreatingResourceSet(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerLookupFailed(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerLookupFailed(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerReleasingResources(String baseName, Assembly mainAssembly) {
+ if (IsEnabled())
+ ResourceManagerReleasingResources(baseName, GetName(mainAssembly));
+ }
+
+ [NonEvent]
+ public void ResourceManagerNeutralResourcesNotFound(String baseName, Assembly mainAssembly, String resName) {
+ if (IsEnabled())
+ ResourceManagerNeutralResourcesNotFound(baseName, GetName(mainAssembly), resName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerNeutralResourcesFound(String baseName, Assembly mainAssembly, String resName) {
+ if (IsEnabled())
+ ResourceManagerNeutralResourcesFound(baseName, GetName(mainAssembly), resName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerAddingCultureFromConfigFile(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerAddingCultureFromConfigFile(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerCultureNotFoundInConfigFile(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerCultureNotFoundInConfigFile(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ [NonEvent]
+ public void ResourceManagerCultureFoundInConfigFile(String baseName, Assembly mainAssembly, String cultureName) {
+ if (IsEnabled())
+ ResourceManagerCultureFoundInConfigFile(baseName, GetName(mainAssembly), cultureName);
+ }
+
+ private static string GetName(Assembly assembly) {
+ if (assembly == null)
+ return "<<NULL>>";
+ else
+ return assembly.FullName;
+ }
+
+ [Event(30, Level = EventLevel.Verbose, Keywords = Keywords.ThreadPool|Keywords.ThreadTransfer)]
+ public void ThreadPoolEnqueueWork(long workID) {
+ WriteEvent(30, workID);
+ }
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void ThreadPoolEnqueueWorkObject(object workID) {
+ // convert the Object Id to a long
+ ThreadPoolEnqueueWork((long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref workID)));
+ }
+
+ [Event(31, Level = EventLevel.Verbose, Keywords = Keywords.ThreadPool|Keywords.ThreadTransfer)]
+ public void ThreadPoolDequeueWork(long workID) {
+ WriteEvent(31, workID);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void ThreadPoolDequeueWorkObject(object workID) {
+ // convert the Object Id to a long
+ ThreadPoolDequeueWork((long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref workID)));
+ }
+
+ // In the desktop runtime they don't use Tasks for the point at which the response happens, which means that the
+ // Activity ID created by start using implicit activity IDs does not match. Thus disable implicit activities (until we fix that)
+ [Event(140, Level = EventLevel.Informational, Keywords = Keywords.NetClient, ActivityOptions=EventActivityOptions.Disable,
+ Task = Tasks.GetResponse, Opcode = EventOpcode.Start, Version = 1)]
+ private void GetResponseStart(long id, string uri, bool success, bool synchronous) {
+ WriteEvent(140, id, uri, success, synchronous);
+ }
+
+ [Event(141, Level = EventLevel.Informational, Keywords = Keywords.NetClient, ActivityOptions=EventActivityOptions.Disable,
+ Task = Tasks.GetResponse, Opcode = EventOpcode.Stop, Version = 1)]
+ private void GetResponseStop(long id, bool success, bool synchronous, int statusCode) {
+ WriteEvent(141, id, success, synchronous, statusCode);
+ }
+
+ // In the desktop runtime they don't use Tasks for the point at which the response happens, which means that the
+ // Activity ID created by start using implicit activity IDs does not match. Thus disable implicit activities (until we fix that)
+ [Event(142, Level = EventLevel.Informational, Keywords = Keywords.NetClient, ActivityOptions=EventActivityOptions.Disable,
+ Task = Tasks.GetRequestStream, Opcode = EventOpcode.Start, Version = 1)]
+ private void GetRequestStreamStart(long id, string uri, bool success, bool synchronous) {
+ WriteEvent(142, id, uri, success, synchronous);
+ }
+
+ [Event(143, Level = EventLevel.Informational, Keywords = Keywords.NetClient, ActivityOptions=EventActivityOptions.Disable,
+ Task = Tasks.GetRequestStream, Opcode = EventOpcode.Stop, Version = 1)]
+ private void GetRequestStreamStop(long id, bool success, bool synchronous) {
+ WriteEvent(143, id, success, synchronous);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void BeginGetResponse(object id, string uri, bool success, bool synchronous) {
+ if (IsEnabled())
+ GetResponseStart(IdForObject(id), uri, success, synchronous);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void EndGetResponse(object id, bool success, bool synchronous, int statusCode) {
+ if (IsEnabled())
+ GetResponseStop(IdForObject(id), success, synchronous, statusCode);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void BeginGetRequestStream(object id, string uri, bool success, bool synchronous) {
+ if (IsEnabled())
+ GetRequestStreamStart(IdForObject(id), uri, success, synchronous);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void EndGetRequestStream(object id, bool success, bool synchronous) {
+ if (IsEnabled())
+ GetRequestStreamStop(IdForObject(id), success, synchronous);
+ }
+
+ // id - represents a correlation ID that allows correlation of two activities, one stamped by
+ // ThreadTransferSend, the other by ThreadTransferReceive
+ // kind - identifies the transfer: values below 64 are reserved for the runtime. Currently used values:
+ // 1 - managed Timers ("roaming" ID)
+ // 2 - managed async IO operations (FileStream, PipeStream, a.o.)
+ // 3 - WinRT dispatch operations
+ // info - any additional information user code might consider interesting
+ [Event(150, Level = EventLevel.Informational, Keywords = Keywords.ThreadTransfer, Task = Tasks.ThreadTransfer, Opcode = EventOpcode.Send)]
+ public void ThreadTransferSend(long id, int kind, string info, bool multiDequeues) {
+ if (IsEnabled())
+ WriteEvent(150, id, kind, info, multiDequeues);
+ }
+ // id - is a managed object. it gets translated to the object's address. ETW listeners must
+ // keep track of GC movements in order to correlate the value passed to XyzSend with the
+ // (possibly changed) value passed to XyzReceive
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void ThreadTransferSendObj(object id, int kind, string info, bool multiDequeues) {
+ ThreadTransferSend((long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref id)), kind, info, multiDequeues);
+ }
+
+ // id - represents a correlation ID that allows correlation of two activities, one stamped by
+ // ThreadTransferSend, the other by ThreadTransferReceive
+ // kind - identifies the transfer: values below 64 are reserved for the runtime. Currently used values:
+ // 1 - managed Timers ("roaming" ID)
+ // 2 - managed async IO operations (FileStream, PipeStream, a.o.)
+ // 3 - WinRT dispatch operations
+ // info - any additional information user code might consider interesting
+ [Event(151, Level = EventLevel.Informational, Keywords = Keywords.ThreadTransfer, Task = Tasks.ThreadTransfer, Opcode = EventOpcode.Receive)]
+ public void ThreadTransferReceive(long id, int kind, string info) {
+ if (IsEnabled())
+ WriteEvent(151, id, kind, info);
+ }
+ // id - is a managed object. it gets translated to the object's address. ETW listeners must
+ // keep track of GC movements in order to correlate the value passed to XyzSend with the
+ // (possibly changed) value passed to XyzReceive
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void ThreadTransferReceiveObj(object id, int kind, string info) {
+ ThreadTransferReceive((long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref id)), kind, info);
+ }
+
+ // id - represents a correlation ID that allows correlation of two activities, one stamped by
+ // ThreadTransferSend, the other by ThreadTransferReceive
+ // kind - identifies the transfer: values below 64 are reserved for the runtime. Currently used values:
+ // 1 - managed Timers ("roaming" ID)
+ // 2 - managed async IO operations (FileStream, PipeStream, a.o.)
+ // 3 - WinRT dispatch operations
+ // info - any additional information user code might consider interesting
+ [Event(152, Level = EventLevel.Informational, Keywords = Keywords.ThreadTransfer, Task = Tasks.ThreadTransfer, Opcode = Opcodes.ReceiveHandled)]
+ public void ThreadTransferReceiveHandled(long id, int kind, string info) {
+ if (IsEnabled())
+ WriteEvent(152, id, kind, info);
+ }
+ // id - is a managed object. it gets translated to the object's address. ETW listeners must
+ // keep track of GC movements in order to correlate the value passed to XyzSend with the
+ // (possibly changed) value passed to XyzReceive
+ [NonEvent, System.Security.SecuritySafeCritical]
+ public unsafe void ThreadTransferReceiveHandledObj(object id, int kind, string info) {
+ ThreadTransferReceive((long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref id)), kind, info);
+ }
+
+ // return a stable ID for a an object. We use the hash code which is not truely unique but is
+ // close enough for now at least. we add to it 0x7FFFFFFF00000000 to make it distinguishable
+ // from the style of ID that simply casts the object reference to a long (since old versions of the
+ // runtime will emit IDs of that form).
+ private static long IdForObject(object obj) {
+ return obj.GetHashCode() + 0x7FFFFFFF00000000;
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs b/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs
new file mode 100644
index 0000000000..e090c4f106
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs
@@ -0,0 +1,373 @@
+// 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.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 || PROJECTN)
+ [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 && !PROJECTN)
+
+ //
+ // 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 bool IsValueType(this Type type) { return type.IsValueType; }
+ public static bool IsGenericType(this Type type) { return type.IsGenericType; }
+ 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 bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; }
+ public static bool IsGenericType(this Type type) { return type.IsConstructedGenericType; }
+ public static Type BaseType(this Type type) { return type.GetTypeInfo().BaseType; }
+ public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; }
+ public static IEnumerable<PropertyInfo> GetProperties(this Type type) { return type.GetRuntimeProperties(); }
+ public static MethodInfo GetGetMethod(this PropertyInfo propInfo) { return propInfo.GetMethod; }
+ public static Type[] GetGenericArguments(this Type type) { return type.GenericTypeArguments; }
+
+ 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 || PROJECTN
+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
+
+#if PROJECTN
+namespace System
+{
+ public static class AppDomain
+ {
+ public static int GetCurrentThreadId()
+ {
+ return (int)Interop.mincore.GetCurrentThreadId();
+ }
+ }
+}
+#endif
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ArrayTypeInfo.cs
new file mode 100644
index 0000000000..5771354f67
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSet.cs
new file mode 100644
index 0000000000..76c01c6c06
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/ConcurrentSetItem.cs
new file mode 100644
index 0000000000..558dbf670b
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs
new file mode 100644
index 0000000000..d0d687e8d8
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs
@@ -0,0 +1,319 @@
+// 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>
+ [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(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, 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)
+ {
+ /*
+ 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/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EmptyStruct.cs
new file mode 100644
index 0000000000..bc7fb8c346
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumHelper.cs
new file mode 100644
index 0000000000..7a23378bb1
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EnumerableTypeInfo.cs
new file mode 100644
index 0000000000..74a3fa27b2
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventDataAttribute.cs
new file mode 100644
index 0000000000..cdedf13c64
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldAttribute.cs
new file mode 100644
index 0000000000..1a298c2851
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventFieldFormat.cs
new file mode 100644
index 0000000000..fd77b07965
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventIgnoreAttribute.cs
new file mode 100644
index 0000000000..769345f78e
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/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/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs
new file mode 100644
index 0000000000..be97447301
--- /dev/null
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/EventPayload.cs
@@ -0,0 +1,154 @@
+// 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;
+
+#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; } }