Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6926 lines (6319 sloc) 321 KB
// Copyright (c) Microsoft Corporation. All rights reserved
// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
// It is available from http://www.codeplex.com/hyperAddin
#define FEATURE_MANAGED_ETW
#if !ES_BUILD_STANDALONE
#define FEATURE_ACTIVITYSAMPLING
#endif // !ES_BUILD_STANDALONE
#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.
//
//
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 Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
#else
using EventDescriptor = System.Diagnostics.Tracing.EventDescriptor;
#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
{
/// <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("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(Environment.GetResourceString("Argument_InvalidTypeName"), "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("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("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(Environment.GetResourceString("EventSource_InvalidCommand"), "command");
eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
}
// 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 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
}
if (System.Threading.Tasks.TplEtwProvider.Log != null)
System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId);
}
/// <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;
// 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);
// We don't call the activityDying callback here because the caller has declared that
// it is not dying.
if (System.Threading.Tasks.TplEtwProvider.Log != null)
System.Threading.Tasks.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();
UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
ref retVal);
return retVal;
}
}
#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
// Managed thread IDs are more aggressively re-used than native thread IDs,
// so we'll use the latter...
return new Guid(unchecked((uint)AppDomain.GetCurrentThreadId()),
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 Environment.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);
var myType = this.GetType();
Initialize(GetGuid(myType), GetName(myType), traits);
}
/// <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)
{
if (arg1 == null) arg1 = new byte[0];
int blobSize = arg1.Length;
fixed (byte* blob = &arg1[0])
{
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
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)
{
if (arg2 == null) arg2 = new byte[0];
int blobSize = arg2.Length;
fixed (byte* blob = &arg2[0])
{
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
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 unsafe IntPtr DataPointer
{
[SecuritySafeCritical]
get { return (IntPtr)(void*)m_Ptr; }
set { m_Ptr = unchecked((ulong)(void*)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 = (ulong)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 ulong 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())));
//
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;
}
/// <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 (m_provider == null)
{
ThrowEventSourceException(eventName);
}
else
{
if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
ThrowEventSourceException(eventName);
}
}
// 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(Environment.GetResourceString("TraitEven"), "traits");
if (eventSourceGuid == Guid.Empty)
throw new ArgumentException(Environment.GetResourceString("EventSource_NeedGuid"));
if (eventSourceName == null)
throw new ArgumentException(Environment.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;
#endif
#if !ES_BUILD_STANDALONE
// 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;
fixed (void* providerMetadata = this.providerMetadata)
{
setInformationResult = m_provider.SetInformation(
UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
providerMetadata,
this.providerMetadata.Length);
}
}
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("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)
{
IntPtr dataPointer = data->DataPointer;
// advance to next EventData in array
++data;
Type dataType = m_eventData[eventId].Parameters[parameterId].ParameterType;
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*))
{
//
return null;
}
else
{
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);
}
}
// 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(Environment.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())));
//
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);
}
//
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
// 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)
{
// 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, Environment.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
}
}
private int GetParamLengthIncludingByteArray(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 = GetParamLengthIncludingByteArray(m_eventData[eventId].Parameters);
if (eventDataCount != modifiedParamCount)
{
ReportOutOfBandMessage(Environment.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 (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));
}
}
}
}
/// <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 + "\"";
}
//
switch (EventProvider.GetLastWriteEventError())
{
case EventProvider.WriteEventErrorCode.EventTooBig:
ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_EventTooBig"), true);
if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"), innerEx);
break;
case EventProvider.WriteEventErrorCode.NoFreeBuffers:
ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true);
if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
break;
case EventProvider.WriteEventErrorCode.NullInput:
ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_NullInput"), true);
if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"), innerEx);
break;
case EventProvider.WriteEventErrorCode.TooManyArgs:
ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_TooManyArgs"), true);
if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.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))
{
return EventOpcode.Start;
}
else if (eventName.EndsWith(s_ActivityStopSuffix))
{
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 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; //
public TraceLoggingEventTypes TraceLoggingEventTypes;
public EventActivityOptions ActivityOptions;
};
// 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 (m_provider == null) // If we failed to construct
return;
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(Environment.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(Environment.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)
{
//
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_provider == null || m_provider.m_disposed; } }
[SecuritySafeCritical]
private void EnsureDescriptorsInitialized()
{
#if !ES_BUILD_STANDALONE
Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif
if (m_eventData == null)
{
Contract.Assert(m_rawManifest == null);
m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
Contract.Assert(m_eventData != null);
//
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(Environment.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
return success;
}
#if ES_BUILD_PCL
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
// 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)