1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
6 // It is available from http://www.codeplex.com/hyperAddin
9 #define FEATURE_MANAGED_ETW
11 #if !ES_BUILD_STANDALONE && !CORECLR && !ES_BUILD_PN
12 #define FEATURE_ACTIVITYSAMPLING
13 #endif // !ES_BUILD_STANDALONE
15 #endif // PLATFORM_WINDOWS
17 #if ES_BUILD_STANDALONE
18 #define FEATURE_MANAGED_ETW_CHANNELS
19 // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
22 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
24 // Over the years EventSource has become more complex and so it is important to understand
25 // the basic structure of the code to insure that it does not grow more complex.
29 // PRINCIPLE: EventSource - ETW decoupling
31 // Conceptually and EventSouce is something takes event logging data from the source methods
32 // To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
33 // KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
34 // we will call the EtwEventListener, that forwards commands from ETW to EventSources and
35 // listeners to the EventSources and forwards on those events to ETW. THus the model should
36 // be that you DON'T NEED ETW.
38 // Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
39 // to it directly, but this can be VIEWED AS AN OPTIMIATION.
41 // Basic Event Data Flow:
43 // There are two ways for event Data to enter the system
44 // 1) WriteEvent* and friends. This is called the 'contract' based approach because
45 // you write a method per event which forms a contract that is know at compile time.
46 // In this scheme each event is given an EVENTID (small integer). which is its identity
47 // 2) Write<T> methods. This is called the 'dynamic' approach because new events
48 // can be created on the fly. Event identity is determined by the event NAME, and these
49 // are not quite as efficient at runtime since you have at least a hash table lookup
50 // on every event write.
52 // EventSource-EventListener transfer fully support both ways of writing events (either contract
53 // based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
54 // types. It is suggested, however, that you use the contract based approach when the event scheme
55 // is known at compile time (that is whenever possible). It is more efficient, but more importantly
56 // it makes the contract very explicit, and centralizes all policy about logging. These are good
57 // things. The Write<T> API is really meant for more ad-hoc
61 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
62 // during the transfer. In particular object identity is not preserved, some objects are morphed,
63 // and not all data types are supported. In particular you can pass
65 // A Valid type to log to an EventSource include
66 // * Primitive data types
67 // * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
68 // * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
70 // This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
72 // Explicitly allowed structs include (* New for V4.6)
73 // * Marked with the EventData attribute
74 // * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
75 // * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
77 // When classes are returned in an EventListener, what is returned is something that implements
78 // IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
79 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
80 // are obvious NOT the original objects.
82 // ETWserialization formats:
84 // As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
85 // copy/morph of that data as described above. In addition the .NET framework supports a conceptual
86 // ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
87 // to be serialized in a way that ETW supports. ETW supports the following serialization formats
89 // 1) Manifest Based serialization.
90 // 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
92 // A key factor is that the Write<T> method, which support on the fly definition of events, can't
93 // support the manifest based serialization because the manifest needs the schema of all events
94 // to be known before any events are emitted. This implies the following
96 // If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
97 // If you use the EventSource(string) constructor for an eventSource (in which you don't
98 // create a subclass), the default is also to use Self-Describing serialization. In addition
99 // you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
100 // Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
102 // Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
104 // *************************************************************************************
105 // *** INTERNALS: Event Propagation
107 // Data enters the system either though
109 // 1) A user defined method in the user defined subclass of EventSource which calls
110 // A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
111 // * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
112 // B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
113 // C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
115 // All event data eventually flows to one of
116 // * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
117 // * WriteEventVarargs(ID, Guid*, object[])
119 // 2) A call to one of the overloads of Write<T>. All these overloads end up in
120 // * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
122 // On output there are the following routines
123 // Writing to all listeners that are NOT ETW, we have the following routines
124 // * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
125 // * WriteToAllListeners(ID, Guid*, object[])
126 // * WriteToAllListeners(NAME, Guid*, EventPayload)
128 // EventPayload is the internal type that implements the IDictionary<string, object> interface
129 // The EventListeners will pass back for serialized classes for nested object, but
130 // WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
131 // were parameters to a method.
133 // The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
135 // Writing to ETW, Manifest Based
136 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
137 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
138 // Writing to ETW, Self-Describing format
139 // WriteMultiMerge(NAME, Options, Types, EventData*)
140 // WriteMultiMerge(NAME, Options, Types, object[])
141 // WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
144 // All ETW writes eventually call
145 // EventWriteTransfer (native PINVOKE wrapper)
146 // EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
147 // EventProvider.WriteEventRaw - sets last error
148 // EventSource.WriteEventRaw - Does EventSource exception handling logic
151 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
152 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
154 // Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
155 // how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
156 // since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
157 // can call one of these
158 // WriteMetadata - transforms the type T into serialization meta data blob for that type
159 // WriteObjectData - transforms an object of T into serialization meta data blob for that type
160 // GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
161 // The first two are used to serialize something for ETW. The second one is used to transform the object
162 // for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
163 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
165 // It is an important observation that while EventSource does support users directly calling with EventData*
166 // blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
167 // path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
169 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
171 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
172 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
173 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
174 // This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
175 // opportunity to expose this format to EventListeners in the future.
178 using System.Runtime.CompilerServices;
179 #if FEATURE_ACTIVITYSAMPLING
180 using System.Collections.Concurrent;
182 using System.Collections.Generic;
183 using System.Collections.ObjectModel;
184 using System.Diagnostics;
185 using System.Diagnostics.CodeAnalysis;
186 using System.Globalization;
187 using System.Reflection;
188 using System.Resources;
189 using System.Security;
190 #if !CORECLR && !ES_BUILD_PN
191 using System.Security.Permissions;
192 #endif // !CORECLR && !ES_BUILD_PN
195 using System.Threading;
196 using Microsoft.Win32;
198 #if ES_BUILD_STANDALONE
199 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
201 using System.Threading.Tasks;
204 using Microsoft.Reflection;
206 #if !ES_BUILD_AGAINST_DOTNET_V35
207 using Contract = System.Diagnostics.Contracts.Contract;
209 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
212 #if CORECLR || ES_BUILD_PN
213 using Internal.Runtime.Augments;
216 #if ES_BUILD_STANDALONE
217 namespace Microsoft.Diagnostics.Tracing
219 namespace System.Diagnostics.Tracing
223 /// This class is meant to be inherited by a user-defined event source in order to define a managed
224 /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
225 /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
226 /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
227 /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
228 /// is sufficient for many users.
230 /// To achieve more control over the ETW provider manifest exposed by the event source type, the
231 /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
233 /// For very advanced EventSources, it is possible to intercept the commands being given to the
234 /// eventSource and change what filtering is done (see EventListener.EnableEvents and
235 /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
236 /// e.g. dumping a data structure (see EventSource.SendCommand and
237 /// <see cref="EventSource.OnEventCommand"/>).
239 /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
240 /// It is also possible to control and intercept the data dispatcher programmatically. See
241 /// <see cref="EventListener"/> for more.
245 /// This is a minimal definition for a custom event source:
247 /// [EventSource(Name="Samples-Demos-Minimal")]
248 /// sealed class MinimalEventSource : EventSource
250 /// public static MinimalEventSource Log = new MinimalEventSource();
251 /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
252 /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
253 /// private MinimalEventSource() {}
257 public partial class EventSource : IDisposable
260 #if FEATURE_EVENTSOURCE_XPLAT
261 private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
262 #endif //FEATURE_EVENTSOURCE_XPLAT
265 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
267 public string Name { get { return m_name; } }
269 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
271 public Guid Guid { get { return m_guid; } }
274 /// Returns true if the eventSource has been enabled at all. This is the prefered test
275 /// to be performed before a relatively expensive EventSource operation.
277 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
278 public bool IsEnabled()
280 return m_eventSourceEnabled;
284 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
286 /// Note that the result of this function is only an approximation on whether a particular
287 /// event is active or not. It is only meant to be used as way of avoiding expensive
288 /// computation for logging when logging is not on, therefore it sometimes returns false
289 /// positives (but is always accurate when returning false). EventSources are free to
290 /// have additional filtering.
292 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
293 public bool IsEnabled(EventLevel level, EventKeywords keywords)
295 return IsEnabled(level, keywords, EventChannel.None);
299 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
300 /// if 'keywords' specifies a channel bit for a channel that is enabled.
302 /// Note that the result of this function only an approximation on whether a particular
303 /// event is active or not. It is only meant to be used as way of avoiding expensive
304 /// computation for logging when logging is not on, therefore it sometimes returns false
305 /// positives (but is always accurate when returning false). EventSources are free to
306 /// have additional filtering.
308 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
309 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
311 if (!m_eventSourceEnabled)
314 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
317 #if !FEATURE_ACTIVITYSAMPLING
321 #else // FEATURE_ACTIVITYSAMPLING
325 #if OPTIMIZE_IS_ENABLED
326 //================================================================================
327 // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
328 // in case activity tracing/sampling is enabled. The added complexity of this
329 // code however weighs against having it "on" until we know it's really needed.
330 // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
331 //================================================================================
333 // At this point we believe the event is enabled, however we now need to check
334 // if we filter because of activity
336 // Optimization, all activity filters also register a delegate here, so if there
337 // is no delegate, we know there are no activity filters, which means that there
338 // is no additional filtering, which means that we can return true immediately.
339 if (s_activityDying == null)
342 // if there's at least one legacy ETW listener we can't filter this
343 if (m_legacySessions != null && m_legacySessions.Count > 0)
346 // if any event ID that triggers a new activity, or "transfers" activities
347 // is covered by 'keywords' we can't filter this
348 if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
351 // See if all listeners have activity filters that would block the event.
352 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
354 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
355 if (etwSession == null)
358 ActivityFilter activityFilter = etwSession.m_activityFilter;
359 if (activityFilter == null ||
360 ActivityFilter.GetFilter(activityFilter, this) == null)
362 // No activity filter for ETW, if event is active for ETW, we can't filter.
363 for (int i = 0; i < m_eventData.Length; i++)
364 if (m_eventData[i].EnabledForETW)
367 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
371 // for regular event listeners
372 var curDispatcher = m_Dispatchers;
373 while (curDispatcher != null)
375 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
376 if (activityFilter == null)
378 // See if any event is enabled.
379 for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
380 if (curDispatcher.m_EventEnabled[i])
383 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
385 curDispatcher = curDispatcher.m_Next;
388 // Every listener has an activity filter that is blocking writing the event,
389 // thus the event is not enabled.
391 #endif // OPTIMIZE_IS_ENABLED
393 #endif // FEATURE_ACTIVITYSAMPLING
397 /// Returns the settings for the event source instance
399 public EventSourceSettings Settings
401 get { return m_config; }
406 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
407 /// This API allows you to compute this without actually creating an instance of the EventSource.
408 /// It only needs to reflect over the type.
410 public static Guid GetGuid(Type eventSourceType)
412 if (eventSourceType == null)
413 throw new ArgumentNullException(nameof(eventSourceType));
414 Contract.EndContractBlock();
416 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
417 string name = eventSourceType.Name;
420 if (attrib.Guid != null)
423 #if !ES_BUILD_AGAINST_DOTNET_V35
424 if (Guid.TryParse(attrib.Guid, out g))
427 try { return new Guid(attrib.Guid); }
428 catch (Exception) { }
432 if (attrib.Name != null)
438 throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), nameof(eventSourceType));
440 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
443 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
444 /// This API allows you to compute this without actually creating an instance of the EventSource.
445 /// It only needs to reflect over the type.
447 public static string GetName(Type eventSourceType)
449 return GetName(eventSourceType, EventManifestOptions.None);
453 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
454 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
455 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
456 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
458 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
459 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
460 /// which it is embedded. This parameter specifies what name will be used</param>
461 /// <returns>The XML data string</returns>
462 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
464 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
467 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
468 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
469 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
470 /// ensures that the entries in the event log will be "optimally" localized.
472 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
473 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
474 /// which it is embedded. This parameter specifies what name will be used</param>
475 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
476 /// this returns null when the eventSourceType does not require explicit registration</param>
477 /// <returns>The XML data string or null</returns>
478 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
480 if (eventSourceType == null)
481 throw new ArgumentNullException(nameof(eventSourceType));
482 Contract.EndContractBlock();
484 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
485 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
488 // EventListener support
490 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
492 /// <returns></returns>
493 public static IEnumerable<EventSource> GetSources()
495 var ret = new List<EventSource>();
496 lock (EventListener.EventListenersLock)
498 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
500 EventSource eventSource = eventSourceRef.Target as EventSource;
501 if (eventSource != null && !eventSource.IsDisposed)
502 ret.Add(eventSource);
509 /// Send a command to a particular EventSource identified by 'eventSource'.
510 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
511 /// callback. What the EventSource does with the command and its arguments are from
512 /// that point EventSource-specific.
514 /// <param name="eventSource">The instance of EventSource to send the command to</param>
515 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
516 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
517 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
519 if (eventSource == null)
520 throw new ArgumentNullException(nameof(eventSource));
522 // User-defined EventCommands should not conflict with the reserved commands.
523 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
525 throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), nameof(command));
528 eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
531 #if !ES_BUILD_STANDALONE
533 /// This property allows EventSource code to appropriately handle as "different"
534 /// activities started on different threads that have not had an activity created on them.
536 internal static Guid InternalCurrentThreadActivityId
540 Guid retval = CurrentThreadActivityId;
541 if (retval == Guid.Empty)
543 retval = FallbackActivityId;
549 internal static Guid FallbackActivityId
553 #pragma warning disable 612, 618
554 int threadID = AppDomain.GetCurrentThreadId();
556 // Managed thread IDs are more aggressively re-used than native thread IDs,
557 // so we'll use the latter...
558 return new Guid(unchecked((uint)threadID),
559 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
560 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
561 #pragma warning restore 612, 618
564 #endif // !ES_BUILD_STANDALONE
566 // Error APIs. (We don't throw by default, but you can probe for status)
570 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
571 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
573 /// The event source constructor does not throw exceptions. Instead we remember any exception that
574 /// was generated (it is also logged to Trace.WriteLine).
576 public Exception ConstructionException { get { return m_constructionException; } }
579 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
580 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
581 /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
582 /// construction time and can be retrieved by using this GetTrait API.
584 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
585 /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
586 public string GetTrait(string key)
588 if (m_traits != null)
590 for (int i = 0; i < m_traits.Length - 1; i += 2)
592 if (m_traits[i] == key)
593 return m_traits[i + 1];
600 /// Displays the name and GUID for the eventSource for debugging purposes.
602 public override string ToString()
604 return Resources.GetResourceString("EventSource_ToString", Name, Guid);
608 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
610 public event EventHandler<EventCommandEventArgs> EventCommandExecuted
614 m_eventCommandExecuted += value;
616 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
617 // It should get a chance to handle the deferred commands.
618 EventCommandEventArgs deferredCommands = m_deferredCommands;
619 while (deferredCommands != null)
621 value(this, deferredCommands);
622 deferredCommands = deferredCommands.nextCommand;
627 m_eventCommandExecuted -= value;
633 /// This is the constructor that most users will use to create their eventSource. It takes
634 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
635 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
636 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
637 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
638 /// the ETW provider name.
640 protected EventSource()
641 : this(EventSourceSettings.EtwManifestEventFormat)
646 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
647 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
648 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
649 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
650 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
651 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
653 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
655 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
656 protected EventSource(bool throwOnEventWriteErrors)
657 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
661 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
663 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
666 /// Construct an EventSource with additional non-default settings.
668 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
669 /// The first string is the key and the second is the value. These are not interpreted by EventSource
670 /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
672 /// <param name="settings">See EventSourceSettings for more.</param>
673 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
674 protected EventSource(EventSourceSettings settings, params string[] traits)
676 m_config = ValidateSettings(settings);
678 Guid eventSourceGuid;
679 string eventSourceName;
681 EventMetadata[] eventDescriptors;
683 GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
685 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
687 var myType = this.GetType();
688 eventSourceGuid = GetGuid(myType);
689 eventSourceName = GetName(myType);
692 Initialize(eventSourceGuid, eventSourceName, traits);
695 internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
698 // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
699 // On other architectures it is a no-op.
701 // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
702 // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
704 // This will be implemented by an IL rewriter, so we can't make this method abstract or the initial build of the subclass would fail.
706 eventSourceGuid = Guid.Empty;
707 eventSourceName = null;
709 manifestBytes = null;
715 /// This method is called when the eventSource is updated by the controller.
717 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
719 #pragma warning disable 1591
720 // optimized for common signatures (no args)
721 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
722 protected unsafe void WriteEvent(int eventId)
724 WriteEventCore(eventId, 0, null);
727 // optimized for common signatures (ints)
728 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
729 protected unsafe void WriteEvent(int eventId, int arg1)
731 if (m_eventSourceEnabled)
733 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
734 descrs[0].DataPointer = (IntPtr)(&arg1);
736 WriteEventCore(eventId, 1, descrs);
740 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
741 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
743 if (m_eventSourceEnabled)
745 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
746 descrs[0].DataPointer = (IntPtr)(&arg1);
748 descrs[1].DataPointer = (IntPtr)(&arg2);
750 WriteEventCore(eventId, 2, descrs);
754 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
755 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
757 if (m_eventSourceEnabled)
759 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
760 descrs[0].DataPointer = (IntPtr)(&arg1);
762 descrs[1].DataPointer = (IntPtr)(&arg2);
764 descrs[2].DataPointer = (IntPtr)(&arg3);
766 WriteEventCore(eventId, 3, descrs);
770 // optimized for common signatures (longs)
771 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
772 protected unsafe void WriteEvent(int eventId, long arg1)
774 if (m_eventSourceEnabled)
776 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
777 descrs[0].DataPointer = (IntPtr)(&arg1);
779 WriteEventCore(eventId, 1, descrs);
783 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
784 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
786 if (m_eventSourceEnabled)
788 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
789 descrs[0].DataPointer = (IntPtr)(&arg1);
791 descrs[1].DataPointer = (IntPtr)(&arg2);
793 WriteEventCore(eventId, 2, descrs);
797 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
798 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
800 if (m_eventSourceEnabled)
802 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
803 descrs[0].DataPointer = (IntPtr)(&arg1);
805 descrs[1].DataPointer = (IntPtr)(&arg2);
807 descrs[2].DataPointer = (IntPtr)(&arg3);
809 WriteEventCore(eventId, 3, descrs);
813 // optimized for common signatures (strings)
814 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
815 protected unsafe void WriteEvent(int eventId, string arg1)
817 if (m_eventSourceEnabled)
819 if (arg1 == null) arg1 = "";
820 fixed (char* string1Bytes = arg1)
822 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
823 descrs[0].DataPointer = (IntPtr)string1Bytes;
824 descrs[0].Size = ((arg1.Length + 1) * 2);
825 WriteEventCore(eventId, 1, descrs);
830 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
831 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
833 if (m_eventSourceEnabled)
835 if (arg1 == null) arg1 = "";
836 if (arg2 == null) arg2 = "";
837 fixed (char* string1Bytes = arg1)
838 fixed (char* string2Bytes = arg2)
840 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
841 descrs[0].DataPointer = (IntPtr)string1Bytes;
842 descrs[0].Size = ((arg1.Length + 1) * 2);
843 descrs[1].DataPointer = (IntPtr)string2Bytes;
844 descrs[1].Size = ((arg2.Length + 1) * 2);
845 WriteEventCore(eventId, 2, descrs);
850 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
851 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
853 if (m_eventSourceEnabled)
855 if (arg1 == null) arg1 = "";
856 if (arg2 == null) arg2 = "";
857 if (arg3 == null) arg3 = "";
858 fixed (char* string1Bytes = arg1)
859 fixed (char* string2Bytes = arg2)
860 fixed (char* string3Bytes = arg3)
862 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
863 descrs[0].DataPointer = (IntPtr)string1Bytes;
864 descrs[0].Size = ((arg1.Length + 1) * 2);
865 descrs[1].DataPointer = (IntPtr)string2Bytes;
866 descrs[1].Size = ((arg2.Length + 1) * 2);
867 descrs[2].DataPointer = (IntPtr)string3Bytes;
868 descrs[2].Size = ((arg3.Length + 1) * 2);
869 WriteEventCore(eventId, 3, descrs);
874 // optimized for common signatures (string and ints)
875 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
876 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
878 if (m_eventSourceEnabled)
880 if (arg1 == null) arg1 = "";
881 fixed (char* string1Bytes = arg1)
883 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
884 descrs[0].DataPointer = (IntPtr)string1Bytes;
885 descrs[0].Size = ((arg1.Length + 1) * 2);
886 descrs[1].DataPointer = (IntPtr)(&arg2);
888 WriteEventCore(eventId, 2, descrs);
893 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
894 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
896 if (m_eventSourceEnabled)
898 if (arg1 == null) arg1 = "";
899 fixed (char* string1Bytes = arg1)
901 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
902 descrs[0].DataPointer = (IntPtr)string1Bytes;
903 descrs[0].Size = ((arg1.Length + 1) * 2);
904 descrs[1].DataPointer = (IntPtr)(&arg2);
906 descrs[2].DataPointer = (IntPtr)(&arg3);
908 WriteEventCore(eventId, 3, descrs);
913 // optimized for common signatures (string and longs)
914 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
915 protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
917 if (m_eventSourceEnabled)
919 if (arg1 == null) arg1 = "";
920 fixed (char* string1Bytes = arg1)
922 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
923 descrs[0].DataPointer = (IntPtr)string1Bytes;
924 descrs[0].Size = ((arg1.Length + 1) * 2);
925 descrs[1].DataPointer = (IntPtr)(&arg2);
927 WriteEventCore(eventId, 2, descrs);
932 // optimized for common signatures (long and string)
933 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
934 protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
936 if (m_eventSourceEnabled)
938 if (arg2 == null) arg2 = "";
939 fixed (char* string2Bytes = arg2)
941 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
942 descrs[0].DataPointer = (IntPtr)(&arg1);
944 descrs[1].DataPointer = (IntPtr)string2Bytes;
945 descrs[1].Size = ((arg2.Length + 1) * 2);
946 WriteEventCore(eventId, 2, descrs);
951 // optimized for common signatures (int and string)
952 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
953 protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
955 if (m_eventSourceEnabled)
957 if (arg2 == null) arg2 = "";
958 fixed (char* string2Bytes = arg2)
960 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
961 descrs[0].DataPointer = (IntPtr)(&arg1);
963 descrs[1].DataPointer = (IntPtr)string2Bytes;
964 descrs[1].Size = ((arg2.Length + 1) * 2);
965 WriteEventCore(eventId, 2, descrs);
970 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
971 protected unsafe void WriteEvent(int eventId, byte[] arg1)
973 if (m_eventSourceEnabled)
975 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
976 if (arg1 == null || arg1.Length == 0)
979 descrs[0].DataPointer = (IntPtr)(&blobSize);
981 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
983 WriteEventCore(eventId, 2, descrs);
987 int blobSize = arg1.Length;
988 fixed (byte* blob = &arg1[0])
990 descrs[0].DataPointer = (IntPtr)(&blobSize);
992 descrs[1].DataPointer = (IntPtr)blob;
993 descrs[1].Size = blobSize;
994 WriteEventCore(eventId, 2, descrs);
1000 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1001 protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
1003 if (m_eventSourceEnabled)
1005 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1006 descrs[0].DataPointer = (IntPtr)(&arg1);
1008 if (arg2 == null || arg2.Length == 0)
1011 descrs[1].DataPointer = (IntPtr)(&blobSize);
1013 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1015 WriteEventCore(eventId, 3, descrs);
1019 int blobSize = arg2.Length;
1020 fixed (byte* blob = &arg2[0])
1022 descrs[1].DataPointer = (IntPtr)(&blobSize);
1024 descrs[2].DataPointer = (IntPtr)blob;
1025 descrs[2].Size = blobSize;
1026 WriteEventCore(eventId, 3, descrs);
1032 #pragma warning restore 1591
1035 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1037 protected internal struct EventData
1040 /// Address where the one argument lives (if this points to managed memory you must ensure the
1041 /// managed object is pinned.
1043 public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
1045 /// Size of the argument referenced by DataPointer
1047 public int Size { get { return m_Size; } set { m_Size = value; } }
1051 /// Initializes the members of this EventData object to point at a previously-pinned
1052 /// tracelogging-compatible metadata blob.
1054 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1055 /// <param name="size">The size of the metadata blob.</param>
1056 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1057 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1059 this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
1061 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1064 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1065 // the way EventWrite wants it.
1066 internal long m_Ptr;
1067 internal int m_Size;
1068 #pragma warning disable 0649
1069 internal int m_Reserved; // Used to pad the size to match the Win32 API
1070 #pragma warning restore 0649
1075 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1076 /// do this, while straightforward, is unsafe.
1080 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1082 /// if (IsEnabled())
1084 /// if (arg2 == null) arg2 = "";
1085 /// fixed (char* string2Bytes = arg2)
1087 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1088 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1089 /// descrs[0].Size = 8;
1090 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1091 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1092 /// WriteEventCore(eventId, 2, descrs);
1098 [CLSCompliant(false)]
1099 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1101 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1105 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1106 /// that you use to do this, while straightforward, is unsafe. The only difference from
1107 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1111 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1113 /// if (IsEnabled())
1115 /// if (arg2 == null) arg2 = "";
1116 /// fixed (char* string2Bytes = arg2)
1118 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1119 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1120 /// descrs[0].Size = 8;
1121 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1122 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1123 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1129 [CLSCompliant(false)]
1130 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1132 if (m_eventSourceEnabled)
1136 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1137 if (relatedActivityId != null)
1138 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1140 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1141 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1142 Guid* pActivityId = null;
1143 Guid activityId = Guid.Empty;
1144 Guid relActivityId = Guid.Empty;
1146 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1147 ((activityOptions & EventActivityOptions.Disable) == 0))
1149 if (opcode == EventOpcode.Start)
1151 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1153 else if (opcode == EventOpcode.Stop)
1155 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1158 if (activityId != Guid.Empty)
1159 pActivityId = &activityId;
1160 if (relActivityId != Guid.Empty)
1161 relatedActivityId = &relActivityId;
1164 #if FEATURE_MANAGED_ETW
1165 if (m_eventData[eventId].EnabledForETW)
1168 #if FEATURE_ACTIVITYSAMPLING
1169 // this code should be kept in sync with WriteEventVarargs().
1170 SessionMask etwSessions = SessionMask.All;
1171 // only compute etwSessions if there are *any* ETW filters enabled...
1172 if ((ulong)m_curLiveSessions != 0)
1173 etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
1174 // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
1175 // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
1177 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1179 if (!SelfDescribingEvents)
1181 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1183 // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
1184 // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
1185 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1186 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1187 // synthesize a new one
1188 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1189 ThrowEventSourceException(m_eventData[eventId].Name);
1193 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1194 // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
1195 // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
1196 // only some of the ETW sessions will receive this event. Synthesize a new
1197 // Descriptor whose Keywords field will have the appropriate bits set.
1198 // etwSessions might be 0, if there are legacy ETW listeners that want this event
1199 var desc = new EventDescriptor(
1200 m_eventData[eventId].Descriptor.EventId,
1201 m_eventData[eventId].Descriptor.Version,
1202 m_eventData[eventId].Descriptor.Channel,
1203 m_eventData[eventId].Descriptor.Level,
1204 m_eventData[eventId].Descriptor.Opcode,
1205 m_eventData[eventId].Descriptor.Task,
1206 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1208 if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1209 ThrowEventSourceException(m_eventData[eventId].Name);
1214 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1217 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1219 m_eventData[eventId].Parameters);
1220 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1223 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1224 // TODO: activity ID support
1225 EventSourceOptions opt = new EventSourceOptions
1227 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1228 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1229 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1232 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1236 if (!SelfDescribingEvents)
1238 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1239 ThrowEventSourceException(m_eventData[eventId].Name);
1243 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1246 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1247 m_eventData[eventId].Tags,
1248 m_eventData[eventId].Parameters);
1249 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1252 EventSourceOptions opt = new EventSourceOptions
1254 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1255 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1256 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1259 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1261 #endif // FEATURE_ACTIVITYSAMPLING
1263 #endif // FEATURE_MANAGED_ETW
1265 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1266 WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
1268 catch (Exception ex)
1270 if (ex is EventSourceException)
1273 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1278 // fallback varags helpers.
1280 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1281 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1282 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1283 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1284 /// check so that the varargs call is not made when the EventSource is not active.
1286 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1287 protected unsafe void WriteEvent(int eventId, params object[] args)
1289 WriteEventVarargs(eventId, null, args);
1293 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1294 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1295 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1296 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1297 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1298 /// check so that the varargs call is not made when the EventSource is not active.
1300 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1302 WriteEventVarargs(eventId, &relatedActivityId, args);
1307 #region IDisposable Members
1309 /// Disposes of an EventSource.
1311 public void Dispose()
1314 GC.SuppressFinalize(this);
1317 /// Disposes of an EventSource.
1320 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1322 /// 1. We may be called more than once: do nothing after the first call.
1323 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1325 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1326 protected virtual void Dispose(bool disposing)
1330 #if FEATURE_MANAGED_ETW
1331 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1332 // even in scenarios with a high volume of ETW events.
1333 if (m_eventSourceEnabled)
1337 SendManifest(m_rawManifest);
1340 { } // If it fails, simply give up.
1341 m_eventSourceEnabled = false;
1343 if (m_provider != null)
1345 m_provider.Dispose();
1350 m_eventSourceEnabled = false;
1351 m_eventSourceDisposed = true;
1354 /// Finalizer for EventSource
1358 this.Dispose(false);
1363 #if FEATURE_ACTIVITYSAMPLING
1364 internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
1366 Debug.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
1368 if (m_eventSourceEnabled)
1370 if (listener == null)
1372 WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
1376 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1377 eventCallbackArgs.EventId = 0;
1378 eventCallbackArgs.Message = msg;
1379 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
1380 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
1381 eventCallbackArgs.EventName = "EventSourceMessage";
1382 listener.OnEventWritten(eventCallbackArgs);
1388 private unsafe void WriteEventRaw(
1390 ref EventDescriptor eventDescriptor,
1392 Guid* relatedActivityID,
1396 #if FEATURE_MANAGED_ETW
1397 if (m_provider == null)
1399 ThrowEventSourceException(eventName);
1403 if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
1404 ThrowEventSourceException(eventName);
1406 #endif // FEATURE_MANAGED_ETW
1409 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1410 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1411 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1412 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1415 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1416 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1418 m_config = ValidateSettings(settings);
1419 Initialize(eventSourceGuid, eventSourceName, traits);
1423 /// This method is responsible for the common initialization path from our constructors. It must
1424 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1425 /// "Log", such an exception would become a cached exception for the initialization of the static
1426 /// member, and any future access to the "Log" would throw the cached exception).
1428 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1429 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1434 if (m_traits != null && m_traits.Length % 2 != 0)
1436 throw new ArgumentException(Resources.GetResourceString("TraitEven"), nameof(traits));
1439 if (eventSourceGuid == Guid.Empty)
1441 throw new ArgumentException(Resources.GetResourceString("EventSource_NeedGuid"));
1444 if (eventSourceName == null)
1446 throw new ArgumentException(Resources.GetResourceString("EventSource_NeedName"));
1449 m_name = eventSourceName;
1450 m_guid = eventSourceGuid;
1451 #if FEATURE_ACTIVITYSAMPLING
1452 m_curLiveSessions = new SessionMask(0);
1453 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
1454 #endif // FEATURE_ACTIVITYSAMPLING
1456 //Enable Implicit Activity tracker
1457 m_activityTracker = ActivityTracker.Instance;
1459 #if FEATURE_MANAGED_ETW
1460 // Create and register our provider traits. We do this early because it is needed to log errors
1461 // In the self-describing event case.
1462 this.InitializeProviderMetadata();
1464 // Register the provider with ETW
1465 var provider = new OverideEventProvider(this);
1466 provider.Register(eventSourceGuid);
1468 // Add the eventSource to the global (weak) list.
1469 // This also sets m_id, which is the index in the list.
1470 EventListener.AddEventSource(this);
1472 #if FEATURE_MANAGED_ETW
1473 // OK if we get this far without an exception, then we can at least write out error messages.
1474 // Set m_provider, which allows this.
1475 m_provider = provider;
1477 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1478 // API available on OS >= Win 8 and patched Win 7.
1479 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1480 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1483 int setInformationResult;
1484 System.Runtime.InteropServices.GCHandle metadataHandle =
1485 System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1486 IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1488 setInformationResult = m_provider.SetInformation(
1489 UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1491 (uint)this.providerMetadata.Length);
1493 metadataHandle.Free();
1495 #endif // FEATURE_MANAGED_ETW
1497 Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1498 // We are logically completely initialized at this point.
1499 m_completelyInited = true;
1503 if (m_constructionException == null)
1504 m_constructionException = e;
1505 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1508 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1509 lock (EventListener.EventListenersLock)
1511 // If there are any deferred commands, we can do them now.
1512 // This is the most likely place for exceptions to happen.
1513 // Note that we are NOT resetting m_deferredCommands to NULL here,
1514 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1515 EventCommandEventArgs deferredCommands = m_deferredCommands;
1516 while (deferredCommands != null)
1518 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1519 deferredCommands = deferredCommands.nextCommand;
1524 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1526 if (eventSourceType == null)
1527 throw new ArgumentNullException(nameof(eventSourceType));
1528 Contract.EndContractBlock();
1530 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1531 if (attrib != null && attrib.Name != null)
1534 return eventSourceType.Name;
1538 /// Implements the SHA1 hashing algorithm. Note that this
1539 /// implementation is for hashing public information. Do not
1540 /// use this code to hash private data, as this implementation does
1541 /// not take any steps to avoid information disclosure.
1543 private struct Sha1ForNonSecretPurposes
1545 private long length; // Total message length in bits
1546 private uint[] w; // Workspace
1547 private int pos; // Length of current chunk in bytes
1550 /// Call Start() to initialize the hash object.
1556 this.w = new uint[85];
1561 this.w[80] = 0x67452301;
1562 this.w[81] = 0xEFCDAB89;
1563 this.w[82] = 0x98BADCFE;
1564 this.w[83] = 0x10325476;
1565 this.w[84] = 0xC3D2E1F0;
1569 /// Adds an input byte to the hash.
1571 /// <param name="input">Data to include in the hash.</param>
1572 public void Append(byte input)
1574 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1575 if (64 == ++this.pos)
1582 /// Adds input bytes to the hash.
1584 /// <param name="input">
1585 /// Data to include in the hash. Must not be null.
1587 public void Append(byte[] input)
1589 foreach (var b in input)
1596 /// Retrieves the hash value.
1597 /// Note that after calling this function, the hash object should
1598 /// be considered uninitialized. Subsequent calls to Append or
1599 /// Finish will produce useless results. Call Start() to
1602 /// <param name="output">
1603 /// Buffer to receive the hash value. Must not be null.
1604 /// Up to 20 bytes of hash will be written to the output buffer.
1605 /// If the buffer is smaller than 20 bytes, the remaining hash
1606 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1607 /// rest of the buffer is left unmodified.
1609 public void Finish(byte[] output)
1611 long l = this.length + 8 * this.pos;
1613 while (this.pos != 56)
1620 this.Append((byte)(l >> 56));
1621 this.Append((byte)(l >> 48));
1622 this.Append((byte)(l >> 40));
1623 this.Append((byte)(l >> 32));
1624 this.Append((byte)(l >> 24));
1625 this.Append((byte)(l >> 16));
1626 this.Append((byte)(l >> 8));
1627 this.Append((byte)l);
1629 int end = output.Length < 20 ? output.Length : 20;
1630 for (int i = 0; i != end; i++)
1632 uint temp = this.w[80 + i / 4];
1633 output[i] = (byte)(temp >> 24);
1634 this.w[80 + i / 4] = temp << 8;
1640 /// Called when this.pos reaches 64.
1642 private void Drain()
1644 for (int i = 16; i != 80; i++)
1646 this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1651 uint a = this.w[80];
1652 uint b = this.w[81];
1653 uint c = this.w[82];
1654 uint d = this.w[83];
1655 uint e = this.w[84];
1657 for (int i = 0; i != 20; i++)
1659 const uint k = 0x5A827999;
1660 uint f = (b & c) | ((~b) & d);
1661 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1664 for (int i = 20; i != 40; i++)
1667 const uint k = 0x6ED9EBA1;
1668 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1671 for (int i = 40; i != 60; i++)
1673 uint f = (b & c) | (b & d) | (c & d);
1674 const uint k = 0x8F1BBCDC;
1675 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1678 for (int i = 60; i != 80; i++)
1681 const uint k = 0xCA62C1D6;
1682 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1692 this.length += 512; // 64 bytes == 512 bits
1696 private static uint Rol1(uint input)
1698 return (input << 1) | (input >> 31);
1701 private static uint Rol5(uint input)
1703 return (input << 5) | (input >> 27);
1706 private static uint Rol30(uint input)
1708 return (input << 30) | (input >> 2);
1712 private static Guid GenerateGuidFromName(string name)
1714 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1715 var hash = new Sha1ForNonSecretPurposes();
1717 hash.Append(namespaceBytes);
1719 Array.Resize(ref bytes, 16);
1722 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1723 return new Guid(bytes);
1726 private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1728 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1729 // the recursion, but can we do this in a robust way?
1731 IntPtr dataPointer = data->DataPointer;
1732 // advance to next EventData in array
1735 Type dataType = GetDataType(m_eventData[eventId], parameterId);
1738 if (dataType == typeof(IntPtr))
1740 return *((IntPtr*)dataPointer);
1742 else if (dataType == typeof(int))
1744 return *((int*)dataPointer);
1746 else if (dataType == typeof(uint))
1748 return *((uint*)dataPointer);
1750 else if (dataType == typeof(long))
1752 return *((long*)dataPointer);
1754 else if (dataType == typeof(ulong))
1756 return *((ulong*)dataPointer);
1758 else if (dataType == typeof(byte))
1760 return *((byte*)dataPointer);
1762 else if (dataType == typeof(sbyte))
1764 return *((sbyte*)dataPointer);
1766 else if (dataType == typeof(short))
1768 return *((short*)dataPointer);
1770 else if (dataType == typeof(ushort))
1772 return *((ushort*)dataPointer);
1774 else if (dataType == typeof(float))
1776 return *((float*)dataPointer);
1778 else if (dataType == typeof(double))
1780 return *((double*)dataPointer);
1782 else if (dataType == typeof(decimal))
1784 return *((decimal*)dataPointer);
1786 else if (dataType == typeof(bool))
1788 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1789 if (*((int*)dataPointer) == 1)
1798 else if (dataType == typeof(Guid))
1800 return *((Guid*)dataPointer);
1802 else if (dataType == typeof(char))
1804 return *((char*)dataPointer);
1806 else if (dataType == typeof(DateTime))
1808 long dateTimeTicks = *((long*)dataPointer);
1809 return DateTime.FromFileTimeUtc(dateTimeTicks);
1811 else if (dataType == typeof(byte[]))
1813 // byte[] are written to EventData* as an int followed by a blob
1814 int cbSize = *((int*)dataPointer);
1815 byte[] blob = new byte[cbSize];
1816 dataPointer = data->DataPointer;
1818 for (int i = 0; i < cbSize; ++i)
1819 blob[i] = *((byte*)(dataPointer + i));
1822 else if (dataType == typeof(byte*))
1824 // TODO: how do we want to handle this? For now we ignore it...
1829 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1836 m_EventSourceInDecodeObject = true;
1838 if (dataType.IsEnum())
1840 dataType = Enum.GetUnderlyingType(dataType);
1845 // Everything else is marshaled as a string.
1846 // ETW strings are NULL-terminated, so marshal everything up to the first
1847 // null in the string.
1848 //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1849 if(dataPointer == IntPtr.Zero)
1854 return new string((char *)dataPointer);
1859 m_EventSourceInDecodeObject = false;
1864 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1866 private EventDispatcher GetDispatcher(EventListener listener)
1868 EventDispatcher dispatcher = m_Dispatchers;
1869 while (dispatcher != null)
1871 if (dispatcher.m_Listener == listener)
1873 dispatcher = dispatcher.m_Next;
1878 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1880 if (m_eventSourceEnabled)
1884 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1885 if (childActivityID != null)
1887 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1889 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1890 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1891 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1892 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1893 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1894 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1895 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1896 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1897 if (!m_eventData[eventId].HasRelatedActivityID)
1899 throw new ArgumentException(Resources.GetResourceString("EventSource_NoRelatedActivityId"));
1903 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1905 Guid* pActivityId = null;
1906 Guid activityId = Guid.Empty;
1907 Guid relatedActivityId = Guid.Empty;
1908 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1909 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1911 if (childActivityID == null &&
1912 ((activityOptions & EventActivityOptions.Disable) == 0))
1914 if (opcode == EventOpcode.Start)
1916 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1918 else if (opcode == EventOpcode.Stop)
1920 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1923 if (activityId != Guid.Empty)
1924 pActivityId = &activityId;
1925 if (relatedActivityId != Guid.Empty)
1926 childActivityID = &relatedActivityId;
1929 #if FEATURE_MANAGED_ETW
1930 if (m_eventData[eventId].EnabledForETW)
1932 #if FEATURE_ACTIVITYSAMPLING
1933 // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
1934 SessionMask etwSessions = SessionMask.All;
1935 // only compute etwSessions if there are *any* ETW filters enabled...
1936 if ((ulong)m_curLiveSessions != 0)
1937 etwSessions = GetEtwSessionMask(eventId, childActivityID);
1939 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1941 if (!SelfDescribingEvents)
1943 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1945 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1946 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1947 // synthesize a new one
1948 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1949 ThrowEventSourceException(m_eventData[eventId].Name);
1953 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1954 // only some of the ETW sessions will receive this event. Synthesize a new
1955 // Descriptor whose Keywords field will have the appropriate bits set.
1956 var desc = new EventDescriptor(
1957 m_eventData[eventId].Descriptor.EventId,
1958 m_eventData[eventId].Descriptor.Version,
1959 m_eventData[eventId].Descriptor.Channel,
1960 m_eventData[eventId].Descriptor.Level,
1961 m_eventData[eventId].Descriptor.Opcode,
1962 m_eventData[eventId].Descriptor.Task,
1963 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1965 if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
1966 ThrowEventSourceException(m_eventData[eventId].Name);
1971 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1974 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1976 m_eventData[eventId].Parameters);
1977 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1980 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1981 // TODO: activity ID support
1982 EventSourceOptions opt = new EventSourceOptions
1984 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1985 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1986 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1989 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1993 if (!SelfDescribingEvents)
1995 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1996 ThrowEventSourceException(m_eventData[eventId].Name);
2000 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
2003 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2005 m_eventData[eventId].Parameters);
2006 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2009 // TODO: activity ID support
2010 EventSourceOptions opt = new EventSourceOptions
2012 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
2013 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2014 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2017 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2019 #endif // FEATURE_ACTIVITYSAMPLING
2021 #endif // FEATURE_MANAGED_ETW
2022 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
2024 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
2025 // Maintain old behavior - object identity is preserved
2026 if (AppContextSwitches.PreserveEventListnerObjectIdentity)
2028 WriteToAllListeners(eventId, childActivityID, args);
2031 #endif // !ES_BUILD_STANDALONE
2033 object[] serializedArgs = SerializeEventArgs(eventId, args);
2034 WriteToAllListeners(eventId, childActivityID, serializedArgs);
2038 catch (Exception ex)
2040 if (ex is EventSourceException)
2043 ThrowEventSourceException(m_eventData[eventId].Name, ex);
2048 unsafe private object[] SerializeEventArgs(int eventId, object[] args)
2050 TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2051 if (eventTypes == null)
2053 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2055 m_eventData[eventId].Parameters);
2056 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2058 var eventData = new object[eventTypes.typeInfos.Length];
2059 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2061 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2067 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2068 /// checks that they in fact match and logs a warning to the debugger if they don't.
2070 /// <param name="infos"></param>
2071 /// <param name="args"></param>
2072 private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
2074 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2075 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2076 // writing to the debugger log on PCL.
2077 bool typesMatch = args.Length == infos.Length;
2080 while (typesMatch && i < args.Length)
2082 Type pType = infos[i].ParameterType;
2084 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2085 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
2086 // argument is null and the parameter type is non-nullable.
2087 if ((args[i] != null && (args[i].GetType() != pType))
2088 || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
2100 System.Diagnostics.Debugger.Log(0, null, Resources.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
2102 #endif //!ES_BUILD_PCL
2105 private int GetParamLenghtIncludingByteArray(ParameterInfo[] parameters)
2108 foreach (ParameterInfo info in parameters)
2110 if (info.ParameterType == typeof(byte[]))
2123 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2125 // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
2126 // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
2127 // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
2128 // we just used the modifiedParamCount here -- so we need both.
2129 int paramCount = m_eventData[eventId].Parameters.Length;
2130 int modifiedParamCount = GetParamLenghtIncludingByteArray(m_eventData[eventId].Parameters);
2131 if (eventDataCount != modifiedParamCount)
2133 ReportOutOfBandMessage(Resources.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
2134 paramCount = Math.Min(paramCount, eventDataCount);
2137 object[] args = new object[paramCount];
2139 EventSource.EventData* dataPtr = data;
2140 for (int i = 0; i < paramCount; i++)
2141 args[i] = DecodeObject(eventId, i, ref dataPtr);
2142 WriteToAllListeners(eventId, childActivityID, args);
2145 // helper for writing to all EventListeners attached the current eventSource.
2146 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
2148 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2149 eventCallbackArgs.EventId = eventId;
2150 if (childActivityID != null)
2151 eventCallbackArgs.RelatedActivityId = *childActivityID;
2152 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2153 eventCallbackArgs.Message = m_eventData[eventId].Message;
2154 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2156 DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2159 private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2161 Exception lastThrownException = null;
2162 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2164 Debug.Assert(dispatcher.m_EventEnabled != null);
2165 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2167 #if FEATURE_ACTIVITYSAMPLING
2168 var activityFilter = dispatcher.m_Listener.m_activityFilter;
2169 // order below is important as PassesActivityFilter will "flow" active activities
2170 // even when the current EventSource doesn't have filtering enabled. This allows
2171 // interesting activities to be updated so that sources that do sample can get
2173 if (activityFilter == null ||
2174 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2175 m_eventData[eventId].TriggersActivityTracking > 0,
2177 !dispatcher.m_activityFilteringEnabled)
2178 #endif // FEATURE_ACTIVITYSAMPLING
2182 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2186 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2187 + e.Message, false);
2188 lastThrownException = e;
2194 if (lastThrownException != null)
2196 throw new EventSourceException(lastThrownException);
2200 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2201 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2203 #if FEATURE_MANAGED_ETW
2204 if (m_provider != null)
2206 string eventName = "EventSourceMessage";
2207 if (SelfDescribingEvents)
2209 EventSourceOptions opt = new EventSourceOptions
2211 Keywords = (EventKeywords)unchecked(keywords),
2214 var msg = new { message = msgString };
2215 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2216 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2220 // We want the name of the provider to show up so if we don't have a manifest we create
2221 // on that at least has the provider name (I don't define any events).
2222 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2224 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2225 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2226 manifestBuilder.AddEventParameter(typeof(string), "message");
2227 manifestBuilder.EndEvent();
2228 SendManifest(manifestBuilder.CreateManifest());
2231 // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2232 fixed (char* msgStringPtr = msgString)
2234 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2235 EventProvider.EventData data = new EventProvider.EventData();
2236 data.Ptr = (ulong)msgStringPtr;
2237 data.Size = (uint)(2 * (msgString.Length + 1));
2239 m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
2243 #endif // FEATURE_MANAGED_ETW
2247 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2248 /// while writing the message to any one of the listeners will be silently ignored.
2250 private void WriteStringToAllListeners(string eventName, string msg)
2252 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2253 eventCallbackArgs.EventId = 0;
2254 eventCallbackArgs.Message = msg;
2255 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2256 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2257 eventCallbackArgs.EventName = eventName;
2259 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2261 bool dispatcherEnabled = false;
2262 if (dispatcher.m_EventEnabled == null)
2264 // if the listeners that weren't correctly initialized, we will send to it
2265 // since this is an error message and we want to see it go out.
2266 dispatcherEnabled = true;
2270 // if there's *any* enabled event on the dispatcher we'll write out the string
2271 // otherwise we'll treat the listener as disabled and skip it
2272 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2274 if (dispatcher.m_EventEnabled[evtId])
2276 dispatcherEnabled = true;
2283 if (dispatcherEnabled)
2284 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2288 // ignore any exceptions thrown by listeners' OnEventWritten
2293 #if FEATURE_ACTIVITYSAMPLING
2294 unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
2296 SessionMask etwSessions = new SessionMask();
2298 for (int i = 0; i < SessionMask.MAX; ++i)
2300 EtwSession etwSession = m_etwSessionIdMap[i];
2301 if (etwSession != null)
2303 ActivityFilter activityFilter = etwSession.m_activityFilter;
2304 // PassesActivityFilter() will flow "interesting" activities, so make sure
2305 // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
2306 // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
2307 // do not fire events indiscriminately, when no filters are specified, but only
2308 // if, in addition, the session did not also enable ActivitySampling)
2309 if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
2310 activityFilter != null &&
2311 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2312 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
2313 !m_activityFilteringForETWEnabled[i])
2315 etwSessions[i] = true;
2319 // flow "interesting" activities for all legacy sessions in which there's some
2320 // level of activity tracing enabled (even other EventSources)
2321 if (m_legacySessions != null && m_legacySessions.Count > 0 &&
2322 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
2324 // only calculate InternalCurrentThreadActivityId once
2325 Guid* pCurrentActivityId = null;
2326 Guid currentActivityId;
2327 foreach (var legacyEtwSession in m_legacySessions)
2329 if (legacyEtwSession == null)
2332 ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
2333 if (activityFilter != null)
2335 if (pCurrentActivityId == null)
2337 currentActivityId = InternalCurrentThreadActivityId;
2338 pCurrentActivityId = ¤tActivityId;
2340 ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
2347 #endif // FEATURE_ACTIVITYSAMPLING
2350 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2351 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2353 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2358 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2359 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2361 #if FEATURE_MANAGED_ETW_CHANNELS
2362 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2364 EventChannel channel = EventChannel.None;
2367 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2370 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2371 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2376 // does is pass the level test?
2377 if ((currentLevel != 0) && (currentLevel < eventLevel))
2380 // if yes, does it pass the keywords test?
2381 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2383 #if FEATURE_MANAGED_ETW_CHANNELS
2384 // is there a channel with keywords that match currentMatchAnyKeyword?
2385 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2387 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2388 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2394 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2401 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2402 private void ThrowEventSourceException(string eventName, Exception innerEx = null)
2404 // If we fail during out of band logging we may end up trying
2405 // to throw another EventSourceException, thus hitting a StackOverflowException.
2406 // Avoid StackOverflow by making sure we do not recursively call this method.
2407 if (m_EventSourceExceptionRecurenceCount > 0)
2411 m_EventSourceExceptionRecurenceCount++;
2413 string errorPrefix = "EventSourceException";
2414 if (eventName != null)
2416 errorPrefix += " while processing event \"" + eventName + "\"";
2419 // TODO Create variations of EventSourceException that indicate more information using the error code.
2420 switch (EventProvider.GetLastWriteEventError())
2422 case EventProvider.WriteEventErrorCode.EventTooBig:
2423 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_EventTooBig"), true);
2424 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_EventTooBig"), innerEx);
2426 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2427 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NoFreeBuffers"), true);
2428 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
2430 case EventProvider.WriteEventErrorCode.NullInput:
2431 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NullInput"), true);
2432 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NullInput"), innerEx);
2434 case EventProvider.WriteEventErrorCode.TooManyArgs:
2435 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_TooManyArgs"), true);
2436 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_TooManyArgs"), innerEx);
2439 if (innerEx != null)
2440 ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2442 ReportOutOfBandMessage(errorPrefix, true);
2443 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2449 m_EventSourceExceptionRecurenceCount--;
2453 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
2455 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2456 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2457 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2459 ThrowEventSourceException(eventName);
2463 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2465 if (opcode == EventOpcode.Info && eventName != null)
2467 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2469 return EventOpcode.Start;
2471 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2473 return EventOpcode.Stop;
2480 #if FEATURE_MANAGED_ETW
2482 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2484 private class OverideEventProvider : EventProvider
2486 public OverideEventProvider(EventSource eventSource)
2488 this.m_eventSource = eventSource;
2490 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2491 int perEventSourceSessionId, int etwSessionId)
2493 // We use null to represent the ETW EventListener.
2494 EventListener listener = null;
2495 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
2496 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2498 private EventSource m_eventSource;
2503 /// Used to hold all the static information about an event. This includes everything in the event
2504 /// descriptor as well as some stuff we added specifically for EventSource. see the
2505 /// code:m_eventData for where we use this.
2509 EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2510 now the move to CoreLib marked them as private.
2511 While they are technically private (it's a contract used between the library and the ILC toolchain),
2512 we need them to be rooted and exported from shared library for the system to work.
2513 For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2514 root them and modify shared library definition to force export them.
2521 partial struct EventMetadata
2523 public EventDescriptor Descriptor;
2524 public EventTags Tags;
2525 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2526 public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
2528 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2529 #if !FEATURE_ACTIVITYSAMPLING
2530 #pragma warning disable 0649
2532 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2533 #if !FEATURE_ACTIVITYSAMPLING
2534 #pragma warning restore 0649
2536 public string Name; // the name of the event
2537 public string Message; // If the event has a message associated with it, this is it.
2538 public ParameterInfo[] Parameters; // TODO can we remove?
2540 public TraceLoggingEventTypes TraceLoggingEventTypes;
2541 public EventActivityOptions ActivityOptions;
2544 public EventParameterType[] ParameterTypes;
2548 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2549 // eventSource. The logic is as follows
2551 // * if Command == Update
2552 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2553 // to (if listener != null)
2554 // perEventSourceSessionId = 0 - reserved for EventListeners
2555 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2556 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2557 // Keywords that identifies the session
2558 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2559 // discriminated by etwSessionId
2560 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2561 // activity tracing across different providers (which might have different sessionIds
2562 // for the same ETW session)
2563 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2564 // eventSource. In particular, if 'enabled' is false, 'level' and
2565 // 'matchAnyKeywords' are not used.
2566 // * OnEventCommand is invoked, which may cause calls to
2567 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2568 // depending on the logic in that routine.
2569 // * else (command != Update)
2570 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2571 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2573 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2574 internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
2575 EventCommand command, bool enable,
2576 EventLevel level, EventKeywords matchAnyKeyword,
2577 IDictionary<string, string> commandArguments)
2579 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2580 lock (EventListener.EventListenersLock)
2582 if (m_completelyInited)
2584 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2585 this.m_deferredCommands = null;
2586 // We are fully initialized, do the command
2587 DoCommand(commandArgs);
2591 // We can't do the command, simply remember it and we do it when we are fully constructed.
2592 commandArgs.nextCommand = m_deferredCommands;
2593 m_deferredCommands = commandArgs;
2599 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2600 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2601 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2602 /// This helper actually does all actual command logic.
2604 internal void DoCommand(EventCommandEventArgs commandArgs)
2606 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2607 // We defer commands until we are completely inited. This allows error messages to be sent.
2608 Debug.Assert(m_completelyInited);
2610 #if FEATURE_MANAGED_ETW
2611 if (m_provider == null) // If we failed to construct
2613 #endif // FEATURE_MANAGED_ETW
2615 m_outOfBandMessageCount = 0;
2616 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2619 EnsureDescriptorsInitialized();
2620 Debug.Assert(m_eventData != null);
2622 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2623 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2624 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2626 throw new ArgumentException(Resources.GetResourceString("EventSource_ListenerNotFound"));
2629 if (commandArgs.Arguments == null)
2630 commandArgs.Arguments = new Dictionary<string, string>();
2632 if (commandArgs.Command == EventCommand.Update)
2634 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2635 for (int i = 0; i < m_eventData.Length; i++)
2636 EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2638 if (commandArgs.enable)
2640 if (!m_eventSourceEnabled)
2642 // EventSource turned on for the first time, simply copy the bits.
2643 m_level = commandArgs.level;
2644 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2648 // Already enabled, make it the most verbose of the existing and new filter
2649 if (commandArgs.level > m_level)
2650 m_level = commandArgs.level;
2651 if (commandArgs.matchAnyKeyword == 0)
2652 m_matchAnyKeyword = 0;
2653 else if (m_matchAnyKeyword != 0)
2654 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2658 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2659 // represent 0-based positive values
2660 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2661 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2662 bSessionEnable = false;
2664 if (commandArgs.listener == null)
2666 if (!bSessionEnable)
2667 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2668 // for "global" enable/disable (passed in with listener == null and
2669 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2670 --commandArgs.perEventSourceSessionId;
2673 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2675 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2677 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2678 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2679 Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2681 // Send the manifest if we are enabling an ETW session
2682 if (bSessionEnable && commandArgs.dispatcher == null)
2684 // eventSourceDispatcher == null means this is the ETW manifest
2686 // Note that we unconditionally send the manifest whenever we are enabled, even if
2687 // we were already enabled. This is because there may be multiple sessions active
2688 // and we can't know that all the sessions have seen the manifest.
2689 if (!SelfDescribingEvents)
2690 SendManifest(m_rawManifest);
2693 #if FEATURE_ACTIVITYSAMPLING
2694 if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2696 bool participateInSampling = false;
2697 string activityFilters;
2700 ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
2701 out activityFilters, out sessionIdBit);
2703 if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
2705 throw new ArgumentException(Resources.GetResourceString("EventSource_SessionIdError",
2706 commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
2707 sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
2710 if (commandArgs.listener == null)
2712 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
2716 ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
2717 commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
2720 else if (!bSessionEnable && commandArgs.listener == null)
2722 // if we disable an ETW session, indicate that in a synthesized command argument
2723 if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
2725 commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
2728 #endif // FEATURE_ACTIVITYSAMPLING
2730 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2731 // things like log messages, or test if keywords are enabled in the callback.
2732 if (commandArgs.enable)
2734 Debug.Assert(m_eventData != null);
2735 m_eventSourceEnabled = true;
2738 this.OnEventCommand(commandArgs);
2739 var eventCommandCallback = this.m_eventCommandExecuted;
2740 if (eventCommandCallback != null)
2741 eventCommandCallback(this, commandArgs);
2743 #if FEATURE_ACTIVITYSAMPLING
2744 if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2746 // if we disable an ETW session, complete disabling it
2747 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
2749 #endif // FEATURE_ACTIVITYSAMPLING
2751 if (!commandArgs.enable)
2753 // If we are disabling, maybe we can turn on 'quick checks' to filter
2754 // quickly. These are all just optimizations (since later checks will still filter)
2756 #if FEATURE_ACTIVITYSAMPLING
2757 // Turn off (and forget) any information about Activity Tracing.
2758 if (commandArgs.listener == null)
2760 // reset all filtering information for activity-tracing-aware sessions
2761 for (int i = 0; i < SessionMask.MAX; ++i)
2763 EtwSession etwSession = m_etwSessionIdMap[i];
2764 if (etwSession != null)
2765 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2767 m_activityFilteringForETWEnabled = new SessionMask(0);
2768 m_curLiveSessions = new SessionMask(0);
2769 // reset activity-tracing-aware sessions
2770 if (m_etwSessionIdMap != null)
2771 for (int i = 0; i < SessionMask.MAX; ++i)
2772 m_etwSessionIdMap[i] = null;
2773 // reset legacy sessions
2774 if (m_legacySessions != null)
2775 m_legacySessions.Clear();
2779 ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
2780 commandArgs.dispatcher.m_activityFilteringEnabled = false;
2782 #endif // FEATURE_ACTIVITYSAMPLING
2784 // There is a good chance EnabledForAnyListener are not as accurate as
2785 // they could be, go ahead and get a better estimate.
2786 for (int i = 0; i < m_eventData.Length; i++)
2788 bool isEnabledForAnyListener = false;
2789 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2791 if (dispatcher.m_EventEnabled[i])
2793 isEnabledForAnyListener = true;
2797 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2800 // If no events are enabled, disable the global enabled bit.
2801 if (!AnyEventEnabled())
2804 m_matchAnyKeyword = 0;
2805 m_eventSourceEnabled = false;
2808 #if FEATURE_ACTIVITYSAMPLING
2809 UpdateKwdTriggers(commandArgs.enable);
2810 #endif // FEATURE_ACTIVITYSAMPLING
2814 if (commandArgs.Command == EventCommand.SendManifest)
2816 // TODO: should we generate the manifest here if we hadn't already?
2817 if (m_rawManifest != null)
2818 SendManifest(m_rawManifest);
2821 // These are not used for non-update commands and thus should always be 'default' values
2822 // Debug.Assert(enable == true);
2823 // Debug.Assert(level == EventLevel.LogAlways);
2824 // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2826 this.OnEventCommand(commandArgs);
2827 var eventCommandCallback = m_eventCommandExecuted;
2828 if (eventCommandCallback != null)
2829 eventCommandCallback(this, commandArgs);
2832 #if FEATURE_ACTIVITYSAMPLING
2833 if (m_completelyInited && (commandArgs.listener != null || shouldReport))
2835 SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
2836 ReportActivitySamplingInfo(commandArgs.listener, m);
2838 #endif // FEATURE_ACTIVITYSAMPLING
2842 // When the ETW session is created after the EventSource has registered with the ETW system
2843 // we can send any error messages here.
2844 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2845 // We never throw when doing a command.
2849 #if FEATURE_ACTIVITYSAMPLING
2851 internal void UpdateEtwSession(
2855 string activityFilters,
2856 bool participateInSampling)
2858 if (sessionIdBit < SessionMask.MAX)
2860 // activity-tracing-aware etw session
2863 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2864 ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
2865 m_etwSessionIdMap[sessionIdBit] = etwSession;
2866 m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
2870 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2871 m_etwSessionIdMap[sessionIdBit] = null;
2872 m_activityFilteringForETWEnabled[sessionIdBit] = false;
2873 if (etwSession != null)
2875 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2876 // the ETW session is going away; remove it from the global list
2877 EtwSession.RemoveEtwSession(etwSession);
2880 m_curLiveSessions[sessionIdBit] = bEnable;
2884 // legacy etw session
2887 if (m_legacySessions == null)
2888 m_legacySessions = new List<EtwSession>(8);
2889 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2890 if (!m_legacySessions.Contains(etwSession))
2891 m_legacySessions.Add(etwSession);
2895 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2896 if (etwSession != null)
2898 if (m_legacySessions != null)
2899 m_legacySessions.Remove(etwSession);
2900 // the ETW session is going away; remove it from the global list
2901 EtwSession.RemoveEtwSession(etwSession);
2907 internal static bool ParseCommandArgs(
2908 IDictionary<string, string> commandArguments,
2909 out bool participateInSampling,
2910 out string activityFilters,
2911 out int sessionIdBit)
2914 participateInSampling = false;
2915 string activityFilterString;
2916 if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
2918 // if a start event is specified default the event source to participate in sampling
2919 participateInSampling = true;
2922 if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
2924 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
2925 activityFilterString == "0")
2926 participateInSampling = false;
2928 participateInSampling = true;
2932 int sessionKwd = -1;
2933 if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
2934 !int.TryParse(sSessionKwd, out sessionKwd) ||
2935 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
2936 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
2943 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
2948 internal void UpdateKwdTriggers(bool enable)
2952 // recompute m_keywordTriggers
2953 ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
2955 gKeywords = 0xFFFFffffFFFFffff;
2957 m_keywordTriggers = 0;
2958 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
2960 EtwSession etwSession = m_etwSessionIdMap[sessId];
2961 if (etwSession == null)
2964 ActivityFilter activityFilter = etwSession.m_activityFilter;
2965 ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
2970 m_keywordTriggers = 0;
2974 #endif // FEATURE_ACTIVITYSAMPLING
2977 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2978 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2979 /// range return false, otherwise true.
2981 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
2983 if (dispatcher == null)
2985 if (eventId >= m_eventData.Length)
2987 #if FEATURE_MANAGED_ETW
2988 if (m_provider != null)
2989 m_eventData[eventId].EnabledForETW = value;
2994 if (eventId >= dispatcher.m_EventEnabled.Length)
2996 dispatcher.m_EventEnabled[eventId] = value;
2998 m_eventData[eventId].EnabledForAnyListener = true;
3004 /// Returns true if any event at all is on.
3006 private bool AnyEventEnabled()
3008 for (int i = 0; i < m_eventData.Length; i++)
3009 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
3014 private bool IsDisposed
3016 get { return m_eventSourceDisposed; }
3019 private void EnsureDescriptorsInitialized()
3021 #if !ES_BUILD_STANDALONE
3022 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3024 if (m_eventData == null)
3026 Guid eventSourceGuid = Guid.Empty;
3027 string eventSourceName = null;
3028 EventMetadata[] eventData = null;
3029 byte[] manifest = null;
3031 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
3032 // to the reflection approach.
3033 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
3035 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
3037 // GetMetadata failed, so we have to set it via reflection.
3038 Debug.Assert(m_rawManifest == null);
3039 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
3040 Debug.Assert(m_eventData != null);
3045 // GetMetadata worked, so set the fields as appropriate.
3046 m_name = eventSourceName;
3047 m_guid = eventSourceGuid;
3048 m_eventData = eventData;
3049 m_rawManifest = manifest;
3051 // TODO Enforce singleton pattern
3052 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
3054 EventSource eventSource = eventSourceRef.Target as EventSource;
3055 if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
3057 if (eventSource != this)
3059 throw new ArgumentException(Resources.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
3064 // Make certain all dispatchers also have their arrays initialized
3065 EventDispatcher dispatcher = m_Dispatchers;
3066 while (dispatcher != null)
3068 if (dispatcher.m_EventEnabled == null)
3069 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
3070 dispatcher = dispatcher.m_Next;
3073 if (s_currentPid == 0)
3075 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL && !CORECLR
3076 // for non-BCL EventSource we must assert SecurityPermission
3077 new SecurityPermission(PermissionState.Unrestricted).Assert();
3079 s_currentPid = Win32Native.GetCurrentProcessId();
3083 // Send out the ETW manifest XML out to ETW
3084 // Today, we only send the manifest to ETW, custom listeners don't get it.
3085 private unsafe bool SendManifest(byte[] rawManifest)
3087 bool success = true;
3089 if (rawManifest == null)
3092 Debug.Assert(!SelfDescribingEvents);
3094 #if FEATURE_MANAGED_ETW
3095 fixed (byte* dataPtr = rawManifest)
3097 // we don't want the manifest to show up in the event log channels so we specify as keywords
3098 // everything but the first 8 bits (reserved for the 8 channels)
3099 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
3100 ManifestEnvelope envelope = new ManifestEnvelope();
3102 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
3103 envelope.MajorVersion = 1;
3104 envelope.MinorVersion = 0;
3105 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
3106 int dataLeft = rawManifest.Length;
3107 envelope.ChunkNumber = 0;
3109 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
3111 dataDescrs[0].Ptr = (ulong)&envelope;
3112 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
3113 dataDescrs[0].Reserved = 0;
3115 dataDescrs[1].Ptr = (ulong)dataPtr;
3116 dataDescrs[1].Reserved = 0;
3118 int chunkSize = ManifestEnvelope.MaxChunkSize;
3119 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3120 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3121 while (dataLeft > 0)
3123 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3124 if (m_provider != null)
3126 if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
3128 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3129 // can fail. If we get this failure on the first chunk try again with something smaller
3130 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3131 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3133 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3135 chunkSize = chunkSize / 2;
3136 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3140 if (ThrowOnEventWriteErrors)
3141 ThrowEventSourceException("SendManifest");
3145 dataLeft -= chunkSize;
3146 dataDescrs[1].Ptr += (uint)chunkSize;
3147 envelope.ChunkNumber++;
3149 // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
3150 // 5 chunks, so only the largest manifests will hit the pause.
3151 if ((envelope.ChunkNumber % 5) == 0)
3153 RuntimeThread.Sleep(15);
3157 #endif // FEATURE_MANAGED_ETW
3162 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3164 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3168 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3169 // When that is the case, we have the build the custom assemblies on a member by hand.
3170 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3172 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3174 // Let the runtime to the work for us, since we can execute code in this context.
3175 Attribute firstAttribute = null;
3176 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3178 firstAttribute = (Attribute)attribute;
3181 return firstAttribute;
3184 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3185 // In the reflection only context, we have to do things by hand.
3186 string fullTypeNameToFind = attributeType.FullName;
3188 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3189 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3192 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3194 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
3196 Attribute attr = null;
3198 Debug.Assert(data.ConstructorArguments.Count <= 1);
3200 if (data.ConstructorArguments.Count == 1)
3202 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
3204 else if (data.ConstructorArguments.Count == 0)
3206 attr = (Attribute)Activator.CreateInstance(attributeType);
3211 Type t = attr.GetType();
3213 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3215 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
3216 object value = namedArgument.TypedValue.Value;
3218 if (p.PropertyType.IsEnum)
3220 value = Enum.Parse(p.PropertyType, value.ToString());
3223 p.SetValue(attr, value, null);
3232 #else // ES_BUILD_PCL && ES_BUILD_PN
3233 // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
3234 throw new ArgumentException(Resources.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
3239 /// Evaluates if two related "EventSource"-domain types should be considered the same
3241 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3242 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3243 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3244 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3246 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3247 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3250 // are these the same type?
3251 attributeType == reflectedAttributeType ||
3252 // are the full typenames equal?
3253 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3254 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3255 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3256 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3257 attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
3258 (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
3259 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3260 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
3265 private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3267 // return false for "object" and interfaces
3268 if (eventSourceType.BaseType() == null)
3271 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3274 eventSourceType = eventSourceType.BaseType();
3276 while (eventSourceType != null && eventSourceType.IsAbstract());
3278 if (eventSourceType != null)
3280 if (!allowEventSourceOverride)
3282 if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
3283 !reflectionOnly && eventSourceType != typeof(EventSource))
3288 if (eventSourceType.Name != "EventSource")
3292 return eventSourceType;
3295 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3296 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3297 // at run time. 'source' is the event source to place the descriptors. If it is null,
3298 // then the descriptors are not creaed, and just the manifest is generated.
3299 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
3300 EventManifestOptions flags = EventManifestOptions.None)
3302 ManifestBuilder manifest = null;
3303 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3304 Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3307 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3310 #if DEBUG && ES_BUILD_STANDALONE
3311 TestSupport.TestHooks.MaybeThrow(eventSourceType,
3312 TestSupport.Category.ManifestError,
3313 "EventSource_CreateManifestAndDescriptors",
3314 new ArgumentException("EventSource_CreateManifestAndDescriptors"));
3319 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3320 EventAttribute defaultEventAttribute;
3321 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3322 EventMetadata[] eventData = null;
3323 Dictionary<string, string> eventsByName = null;
3324 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3326 eventData = new EventMetadata[methods.Length + 1];
3327 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3330 // See if we have localization information.
3331 ResourceManager resources = null;
3332 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3333 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3334 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3336 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3339 // Add an entry unconditionally for event ID 0 which will be for a string message.
3340 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3341 manifest.AddEventParameter(typeof(string), "message");
3342 manifest.EndEvent();
3344 // eventSourceType must be sealed and must derive from this EventSource
3345 if ((flags & EventManifestOptions.Strict) != 0)
3347 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3351 manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
3353 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3355 manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
3359 // Collect task, opcode, keyword and channel information
3360 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3361 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3363 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3366 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3367 if (nestedType != null)
3369 if (eventSourceType.IsAbstract())
3371 manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
3375 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3377 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3382 // ensure we have keywords for the session-filtering reserved bits
3384 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3385 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3386 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3387 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3390 if (eventSourceType != typeof(EventSource))
3392 for (int i = 0; i < methods.Length; i++)
3394 MethodInfo method = methods[i];
3395 ParameterInfo[] args = method.GetParameters();
3397 // Get the EventDescriptor (from the Custom attributes)
3398 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3400 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3401 // the only reason of limiting the number of methods considered to be events. This broke a common
3402 // design of having event sources implement specific interfaces. To fix this in a compatible way
3403 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3404 // as they are marked with the [Event] attribute
3405 if (/* method.IsVirtual || */ method.IsStatic)
3410 if (eventSourceType.IsAbstract())
3412 if (eventAttribute != null)
3414 manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
3418 else if (eventAttribute == null)
3420 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3421 // (see Compat comment above)
3422 if (method.ReturnType != typeof(void))
3427 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3428 // (see Compat comment above)
3429 if (method.IsVirtual)
3434 // If we explicitly mark the method as not being an event, then honor that.
3435 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3438 defaultEventAttribute = new EventAttribute(eventId);
3439 eventAttribute = defaultEventAttribute;
3441 else if (eventAttribute.EventId <= 0)
3443 manifest.ManifestError(Resources.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
3444 continue; // don't validate anything else for this event
3446 if (method.Name.LastIndexOf('.') >= 0)
3448 manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
3452 string eventName = method.Name;
3454 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3456 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3457 bool noTask = (eventAttribute.Task == EventTask.None);
3459 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3461 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3462 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3463 if (!eventAttribute.IsOpcodeSet)
3464 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3466 // Make the stop opcode have the same task as the start opcode.
3469 if (eventAttribute.Opcode == EventOpcode.Start)
3471 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3472 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3473 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3475 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3476 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3477 manifest.AddTask(taskName, (int)eventAttribute.Task);
3480 else if (eventAttribute.Opcode == EventOpcode.Stop)
3482 // Find the start associated with this stop event. We require start to be immediately before the stop
3483 int startEventId = eventAttribute.EventId - 1;
3484 if (eventData != null && startEventId < eventData.Length)
3486 Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3487 EventMetadata startEventMetadata = eventData[startEventId];
3489 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3490 // Ideally we would throw an error
3491 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3492 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3493 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3494 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3497 // Make the stop event match the start event
3498 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3502 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3504 throw new ArgumentException(Resources.GetResourceString("EventSource_StopsFollowStarts"));
3510 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3511 if (!(source != null && source.SelfDescribingEvents))
3513 manifest.StartEvent(eventName, eventAttribute);
3514 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3516 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3518 manifest.EndEvent();
3521 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3523 // Do checking for user errors (optional, but not a big deal so we do it).
3524 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3526 #if FEATURE_MANAGED_ETW_CHANNELS
3527 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3528 // and is not required for the manifest
3529 if (eventAttribute.Channel != EventChannel.None)
3533 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3537 string eventKey = "event_" + eventName;
3538 string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3539 // overwrite inline message with the localized message
3540 if (msg != null) eventAttribute.Message = msg;
3542 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3547 // Tell the TraceLogging stuff where to start allocating its own IDs.
3548 NameInfo.ReserveEventIDsBelow(eventId);
3552 TrimEventDescriptors(ref eventData);
3553 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3554 #if FEATURE_MANAGED_ETW_CHANNELS
3555 source.m_channelData = manifest.GetChannelData();
3559 // if this is an abstract event source we've already performed all the validation we can
3560 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3562 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3563 #if FEATURE_MANAGED_ETW_CHANNELS
3564 || manifest.GetChannelData().Length > 0
3568 // if the manifest is not needed and we're not requested to validate the event source return early
3569 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3572 res = manifest.CreateManifest();
3577 // if this is a runtime manifest generation let the exception propagate
3578 if ((flags & EventManifestOptions.Strict) == 0)
3580 // else store it to include it in the Argument exception we raise below
3584 if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3586 string msg = String.Empty;
3587 if (manifest.Errors.Count > 0)
3589 bool firstError = true;
3590 foreach (string error in manifest.Errors)
3593 msg += Environment.NewLine;
3599 msg = "Unexpected error: " + exception.Message;
3601 throw new ArgumentException(msg, exception);
3604 return bNeedsManifest ? res : null;
3607 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3609 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3610 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3611 string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
3613 var newargs = new ParameterInfo[args.Length - 1];
3614 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3623 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3625 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3627 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3628 Type staticFieldType = staticField.FieldType;
3629 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3631 if (providerEnumKind != "Opcodes") goto Error;
3632 int value = (int)staticField.GetRawConstantValue();
3633 manifest.AddOpcode(staticField.Name, value);
3635 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3637 if (providerEnumKind != "Tasks") goto Error;
3638 int value = (int)staticField.GetRawConstantValue();
3639 manifest.AddTask(staticField.Name, value);
3641 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3643 if (providerEnumKind != "Keywords") goto Error;
3644 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3645 manifest.AddKeyword(staticField.Name, value);
3647 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3648 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3650 if (providerEnumKind != "Channels") goto Error;
3651 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3652 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3657 manifest.ManifestError(Resources.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
3660 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3661 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3662 // it is populated if we need to look up message resources
3663 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3664 EventAttribute eventAttribute, ParameterInfo[] eventParameters,
3665 bool hasRelatedActivityID)
3667 if (eventData == null || eventData.Length <= eventAttribute.EventId)
3669 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3670 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3671 eventData = newValues;
3674 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3675 eventAttribute.EventId,
3676 eventAttribute.Version,
3677 #if FEATURE_MANAGED_ETW_CHANNELS
3678 (byte)eventAttribute.Channel,
3682 (byte)eventAttribute.Level,
3683 (byte)eventAttribute.Opcode,
3684 (int)eventAttribute.Task,
3685 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3687 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3688 eventData[eventAttribute.EventId].Name = eventName;
3689 eventData[eventAttribute.EventId].Parameters = eventParameters;
3690 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3691 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3692 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3695 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3696 // size after all event descriptors have been added.
3697 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3699 int idx = eventData.Length;
3703 if (eventData[idx].Descriptor.EventId != 0)
3706 if (eventData.Length - idx > 2) // allow one wasted slot.
3708 EventMetadata[] newValues = new EventMetadata[idx + 1];
3709 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3710 eventData = newValues;
3714 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3715 // when a listener gets attached to a eventSource
3716 internal void AddListener(EventListener listener)
3718 lock (EventListener.EventListenersLock)
3720 bool[] enabledArray = null;
3721 if (m_eventData != null)
3722 enabledArray = new bool[m_eventData.Length];
3723 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3724 listener.OnEventSourceCreated(this);
3728 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3729 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3730 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3731 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3732 ManifestBuilder manifest, EventManifestOptions options)
3734 int evtId = eventAttribute.EventId;
3735 string evtName = method.Name;
3736 int eventArg = GetHelperCallFirstArg(method);
3737 if (eventArg >= 0 && evtId != eventArg)
3739 manifest.ManifestError(Resources.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
3742 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3744 manifest.ManifestError(Resources.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
3747 // We give a task to things if they don't have one.
3748 // TODO this is moderately expensive (N*N). We probably should not even bother....
3749 Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3750 for (int idx = 0; idx < eventData.Length; ++idx)
3752 // skip unused Event IDs.
3753 if (eventData[idx].Name == null)
3756 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3758 manifest.ManifestError(Resources.GetResourceString("EventSource_TaskOpcodePairReused",
3759 evtName, evtId, eventData[idx].Name, idx));
3760 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3761 if ((options & EventManifestOptions.Strict) == 0)
3766 // for non-default event opcodes the user must define a task!
3767 if (eventAttribute.Opcode != EventOpcode.Info)
3769 bool failure = false;
3770 if (eventAttribute.Task == EventTask.None)
3774 // If you have the auto-assigned Task, then you did not explicitly set one.
3775 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3776 // But all other cases we want to catch the omission.
3777 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3778 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3783 manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
3787 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3788 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3789 // by eventRegister.exe)
3790 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3791 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3793 // throw new WarningException(Resources.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
3796 if (eventsByName == null)
3797 eventsByName = new Dictionary<string, string>();
3799 if (eventsByName.ContainsKey(evtName))
3801 manifest.ManifestError(Resources.GetResourceString("EventSource_EventNameReused", evtName), true);
3804 eventsByName[evtName] = evtName;
3808 /// This method looks at the IL and tries to pattern match against the standard
3809 /// 'boilerplate' event body
3811 /// { if (Enabled()) WriteEvent(#, ...) }
3813 /// If the pattern matches, it returns the literal number passed as the first parameter to
3814 /// the WriteEvent. This is used to find common user errors (mismatching this
3815 /// number with the EventAttribute ID). It is only used for validation.
3817 /// <param name="method">The method to probe.</param>
3818 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3819 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3820 static private int GetHelperCallFirstArg(MethodInfo method)
3822 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3823 // Currently searches for the following pattern
3825 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3828 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3830 // NOP // 0 or more times
3833 // If we find this pattern we return the XXX. Otherwise we return -1.
3835 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3837 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3839 for (int idx = 0; idx < instrs.Length;)
3841 switch (instrs[idx])
3864 case 21: // LDC_I4_M1
3865 case 22: // LDC_I4_0
3866 case 23: // LDC_I4_1
3867 case 24: // LDC_I4_2
3868 case 25: // LDC_I4_3
3869 case 26: // LDC_I4_4
3870 case 27: // LDC_I4_5
3871 case 28: // LDC_I4_6
3872 case 29: // LDC_I4_7
3873 case 30: // LDC_I4_8
3874 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3875 retVal = instrs[idx] - 22;
3877 case 31: // LDC_I4_S
3878 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3879 retVal = instrs[idx + 1];
3892 // Is this call just before return?
3893 for (int search = idx + 1; search < instrs.Length; search++)
3895 if (instrs[search] == 42) // RET
3897 if (instrs[search] != 0) // NOP
3903 case 44: // BRFALSE_S
3904 case 45: // BRTRUE_S
3913 case 103: // CONV_I1
3914 case 104: // CONV_I2
3915 case 105: // CONV_I4
3916 case 106: // CONV_I8
3917 case 109: // CONV_U4
3918 case 110: // CONV_U8
3924 case 162: // STELEM_REF
3928 // Covers the CEQ instructions used in debug code for some reason.
3929 if (idx >= instrs.Length || instrs[idx] >= 6)
3933 /* Debug.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3934 " at " + idx + " in method " + method.Name); */
3943 #if false // This routine is not needed at all, it was used for unit test debugging.
3944 [Conditional("DEBUG")]
3945 private static void OutputDebugString(string msg)
3948 msg = msg.TrimEnd('\r', '\n') +
3949 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
3950 System.Diagnostics.Debugger.Log(0, null, msg);
3956 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3957 /// It will do this even if the EventSource is not enabled.
3958 /// TODO remove flush parameter it is not used.
3960 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3961 internal void ReportOutOfBandMessage(string msg, bool flush)
3965 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3966 // send message to debugger without delay
3967 System.Diagnostics.Debugger.Log(0, null, String.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
3970 // Send it to all listeners.
3971 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3972 m_outOfBandMessageCount++;
3975 if (m_outOfBandMessageCount == 16)
3977 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3978 msg = "Reached message limit. End of EventSource error messages.";
3981 WriteEventString(EventLevel.LogAlways, -1, msg);
3982 WriteStringToAllListeners("EventSourceMessage", msg);
3984 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3987 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3989 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3990 EventSourceSettings.EtwSelfDescribingEventFormat;
3991 if ((settings & evtFormatMask) == evtFormatMask)
3993 throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), nameof(settings));
3996 // If you did not explicitly ask for manifest, you get self-describing.
3997 if ((settings & evtFormatMask) == 0)
3998 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
4002 private bool ThrowOnEventWriteErrors
4004 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
4007 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
4008 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
4012 private bool SelfDescribingEvents
4016 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
4017 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
4018 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
4024 m_config |= EventSourceSettings.EtwManifestEventFormat;
4025 m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
4029 m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
4030 m_config &= ~EventSourceSettings.EtwManifestEventFormat;
4035 #if FEATURE_ACTIVITYSAMPLING
4036 private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
4038 Debug.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
4040 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
4042 if (!sessions[perEventSourceSessionId])
4046 if (listener == null)
4048 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
4049 Debug.Assert(etwSession != null);
4050 af = etwSession.m_activityFilter;
4054 af = listener.m_activityFilter;
4060 SessionMask m = new SessionMask();
4061 m[perEventSourceSessionId] = true;
4063 foreach (var t in af.GetFilterAsTuple(m_guid))
4065 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
4068 bool participateInSampling = (listener == null) ?
4069 m_activityFilteringForETWEnabled[perEventSourceSessionId] :
4070 GetDispatcher(listener).m_activityFilteringEnabled;
4071 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
4072 perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
4075 #endif // FEATURE_ACTIVITYSAMPLING
4077 // private instance state
4078 private string m_name; // My friendly name (privided in ctor)
4079 internal int m_id; // A small integer that is unique to this instance.
4080 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
4081 internal volatile EventMetadata[] m_eventData; // None per-event data
4082 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
4084 private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
4086 private EventSourceSettings m_config; // configuration information
4088 private bool m_eventSourceDisposed; // has Dispose been called.
4091 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
4092 internal EventLevel m_level; // highest level enabled by any output dispatcher
4093 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
4095 // Dispatching state
4096 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
4097 #if FEATURE_MANAGED_ETW
4098 private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
4100 private bool m_completelyInited; // The EventSource constructor has returned without exception.
4101 private Exception m_constructionException; // If there was an exception construction, this is it
4102 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
4103 private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
4105 private string[] m_traits; // Used to implement GetTraits
4107 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
4109 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
4112 private static bool m_EventSourceInDecodeObject = false;
4114 #if FEATURE_MANAGED_ETW_CHANNELS
4115 internal volatile ulong[] m_channelData;
4118 #if FEATURE_ACTIVITYSAMPLING
4119 private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
4120 private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
4121 private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
4122 internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
4123 internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
4124 static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
4125 // Also used to mark that activity tracing is on for some case
4126 #endif // FEATURE_ACTIVITYSAMPLING
4128 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
4129 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
4130 ActivityTracker m_activityTracker;
4131 internal const string s_ActivityStartSuffix = "Start";
4132 internal const string s_ActivityStopSuffix = "Stop";
4134 // used for generating GUID from eventsource name
4135 private static readonly byte[] namespaceBytes = new byte[] {
4136 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
4137 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
4144 /// Enables specifying event source configuration options to be used in the EventSource constructor.
4147 public enum EventSourceSettings
4150 /// This specifies none of the special configuration options should be enabled.
4154 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
4156 ThrowOnEventWriteErrors = 1,
4158 /// Setting this option is a directive to the ETW listener should use manifest-based format when
4159 /// firing events. This is the default option when defining a type derived from EventSource
4160 /// (using the protected EventSource constructors).
4161 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4163 EtwManifestEventFormat = 4,
4165 /// Setting this option is a directive to the ETW listener should use self-describing event format
4166 /// when firing events. This is the default option when creating a new instance of the EventSource
4167 /// type (using the public EventSource constructors).
4168 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4170 EtwSelfDescribingEventFormat = 8,
4174 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
4175 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
4176 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
4177 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
4178 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
4179 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
4182 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
4183 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
4184 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
4185 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
4187 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
4188 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
4190 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4191 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4192 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4193 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4194 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4196 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4197 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4198 /// that eventSource what events that dispatcher will receive.
4200 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4201 /// override this method to do something useful with the data.
4203 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4204 /// invariant associated with this callback is that every eventSource gets exactly one
4205 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4206 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4207 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4211 public class EventListener : IDisposable
4213 private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
4216 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4217 /// This can happen for all existing EventSources when the EventListener is created
4218 /// as well as for any EventSources that come into existence after the EventListener
4219 /// has been created.
4221 /// These 'catch up' events are called during the construction of the EventListener.
4222 /// Subclasses need to be prepared for that.
4224 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4225 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4227 public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
4231 CallBackForExistingEventSources(false, value);
4233 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
4237 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
4242 /// This event is raised whenever an event has been written by a EventSource for which
4243 /// the EventListener has enabled events.
4245 public event EventHandler<EventWrittenEventArgs> EventWritten;
4248 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4251 public EventListener()
4253 // This will cause the OnEventSourceCreated callback to fire.
4254 CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener((EventListener)obj));
4258 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4259 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4260 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4261 /// call Dispose when they are done with their logging.
4263 #if ES_BUILD_STANDALONE
4264 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4266 public virtual void Dispose()
4268 lock (EventListenersLock)
4270 if (s_Listeners != null)
4272 if (this == s_Listeners)
4274 EventListener cur = s_Listeners;
4275 s_Listeners = this.m_Next;
4276 RemoveReferencesToListenerInEventSources(cur);
4280 // Find 'this' from the s_Listeners linked list.
4281 EventListener prev = s_Listeners;
4284 EventListener cur = prev.m_Next;
4289 // Found our Listener, remove references to to it in the eventSources
4290 prev.m_Next = cur.m_Next; // Remove entry.
4291 RemoveReferencesToListenerInEventSources(cur);
4301 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4302 // 'cleanup' associated with this object
4305 /// Enable all events from the eventSource identified by 'eventSource' to the current
4306 /// dispatcher that have a verbosity level of 'level' or lower.
4308 /// This call can have the effect of REDUCING the number of events sent to the
4309 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4311 /// This call never has an effect on other EventListeners.
4314 public void EnableEvents(EventSource eventSource, EventLevel level)
4316 EnableEvents(eventSource, level, EventKeywords.None);
4319 /// Enable all events from the eventSource identified by 'eventSource' to the current
4320 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4321 /// matching any of the bits in 'matchAnyKeyword'.
4323 /// This call can have the effect of REDUCING the number of events sent to the
4324 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4325 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4327 /// This call never has an effect on other EventListeners.
4329 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4331 EnableEvents(eventSource, level, matchAnyKeyword, null);
4334 /// Enable all events from the eventSource identified by 'eventSource' to the current
4335 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4336 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4337 /// effect passing additional 'key-value' arguments 'arguments' might have.
4339 /// This call can have the effect of REDUCING the number of events sent to the
4340 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4341 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4343 /// This call never has an effect on other EventListeners.
4345 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
4347 if (eventSource == null)
4349 throw new ArgumentNullException(nameof(eventSource));
4351 Contract.EndContractBlock();
4353 eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4356 /// Disables all events coming from eventSource identified by 'eventSource'.
4358 /// This call never has an effect on other EventListeners.
4360 public void DisableEvents(EventSource eventSource)
4362 if (eventSource == null)
4364 throw new ArgumentNullException(nameof(eventSource));
4366 Contract.EndContractBlock();
4368 eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4372 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4373 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4374 /// it useful to store additional information about each eventSource connected to it,
4375 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4376 /// (growable) array (eg List(T)).
4378 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4381 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4382 /// This can happen for all existing EventSources when the EventListener is created
4383 /// as well as for any EventSources that come into existence after the EventListener
4384 /// has been created.
4386 /// These 'catch up' events are called during the construction of the EventListener.
4387 /// Subclasses need to be prepared for that.
4389 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4390 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4392 /// <param name="eventSource"></param>
4393 internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4395 EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
4396 if (callBack != null)
4398 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4399 args.EventSource = eventSource;
4400 callBack(this, args);
4405 /// This method is called whenever an event has been written by a EventSource for which
4406 /// the EventListener has enabled events.
4408 /// <param name="eventData"></param>
4409 internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4411 EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
4412 if (callBack != null)
4414 callBack(this, eventData);
4421 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4422 /// ID to the eventSource (which is simply the ordinal in the global list).
4424 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4425 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4426 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4427 /// that are in the list). This seems OK since the expectation is that EventSources
4428 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4429 /// global variables).
4431 /// <param name="newEventSource"></param>
4432 internal static void AddEventSource(EventSource newEventSource)
4434 lock (EventListenersLock)
4436 if (s_EventSources == null)
4437 s_EventSources = new List<WeakReference>(2);
4439 if (!s_EventSourceShutdownRegistered)
4441 s_EventSourceShutdownRegistered = true;
4445 // Periodically search the list for existing entries to reuse, this avoids
4446 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4448 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4450 int i = s_EventSources.Count; // Work from the top down.
4454 WeakReference weakRef = s_EventSources[i];
4455 if (!weakRef.IsAlive)
4458 weakRef.Target = newEventSource;
4465 newIndex = s_EventSources.Count;
4466 s_EventSources.Add(new WeakReference(newEventSource));
4468 newEventSource.m_id = newIndex;
4470 // Add every existing dispatcher to the new EventSource
4471 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4472 newEventSource.AddListener(listener);
4478 // Whenver we have async callbacks from native code, there is an ugly issue where
4479 // during .NET shutdown native code could be calling the callback, but the CLR
4480 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4481 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4482 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4483 // do this. This is what this callback is for.
4484 // See bug 724140 for more
4485 private static void DisposeOnShutdown(object sender, EventArgs e)
4487 lock (EventListenersLock)
4489 foreach (var esRef in s_EventSources)
4491 EventSource es = esRef.Target as EventSource;
4499 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4500 /// eventSources in the appdomain.
4502 /// The EventListenersLock must be held before calling this routine.
4504 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4506 #if !ES_BUILD_STANDALONE
4507 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4509 // Foreach existing EventSource in the appdomain
4510 foreach (WeakReference eventSourceRef in s_EventSources)
4512 EventSource eventSource = eventSourceRef.Target as EventSource;
4513 if (eventSource != null)
4515 // Is the first output dispatcher the dispatcher we are removing?
4516 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4517 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4520 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4521 EventDispatcher prev = eventSource.m_Dispatchers;
4524 EventDispatcher cur = prev.m_Next;
4527 Debug.Assert(false, "EventSource did not have a registered EventListener!");
4530 if (cur.m_Listener == listenerToRemove)
4532 prev.m_Next = cur.m_Next; // Remove entry.
4543 /// Checks internal consistency of EventSources/Listeners.
4545 [Conditional("DEBUG")]
4546 internal static void Validate()
4548 lock (EventListenersLock)
4550 // Get all listeners
4551 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4552 EventListener cur = s_Listeners;
4555 allListeners.Add(cur, true);
4559 // For all eventSources
4561 foreach (WeakReference eventSourceRef in s_EventSources)
4564 EventSource eventSource = eventSourceRef.Target as EventSource;
4565 if (eventSource == null)
4567 Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4569 // None listeners on eventSources exist in the dispatcher list.
4570 EventDispatcher dispatcher = eventSource.m_Dispatchers;
4571 while (dispatcher != null)
4573 Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4574 dispatcher = dispatcher.m_Next;
4577 // Every dispatcher is on Dispatcher List of every eventSource.
4578 foreach (EventListener listener in allListeners.Keys)
4580 dispatcher = eventSource.m_Dispatchers;
4583 Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4584 if (dispatcher.m_Listener == listener)
4586 dispatcher = dispatcher.m_Next;
4594 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4595 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4596 /// the lock object)
4598 internal static object EventListenersLock
4602 if (s_EventSources == null)
4603 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4604 return s_EventSources;
4608 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
4610 lock (EventListenersLock)
4612 // Disallow creating EventListener reentrancy.
4613 if (s_CreatingListener)
4615 throw new InvalidOperationException(Resources.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
4620 s_CreatingListener = true;
4622 if (addToListenersList)
4624 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4625 // Those added sources see this listener.
4626 this.m_Next = s_Listeners;
4630 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4631 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4632 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4633 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4635 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4637 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4639 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4640 EventSource eventSource = eventSourceRef.Target as EventSource;
4641 if (eventSource != null)
4643 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4644 args.EventSource = eventSource;
4645 callback(this, args);
4653 s_CreatingListener = false;
4660 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
4661 #if FEATURE_ACTIVITYSAMPLING
4662 internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
4663 #endif // FEATURE_ACTIVITYSAMPLING
4668 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4669 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4671 internal static EventListener s_Listeners;
4673 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4674 /// remove themselves from this list this is a weak list and the GC that removes them may
4675 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4676 /// to filter those out.
4678 internal static List<WeakReference> s_EventSources;
4681 /// Used to disallow reentrancy.
4683 private static bool s_CreatingListener = false;
4686 /// Used to register AD/Process shutdown callbacks.
4688 private static bool s_EventSourceShutdownRegistered = false;
4693 /// Passed to the code:EventSource.OnEventCommand callback
4695 public class EventCommandEventArgs : EventArgs
4698 /// Gets the command for the callback.
4700 public EventCommand Command { get; internal set; }
4703 /// Gets the arguments for the callback.
4705 public IDictionary<String, String> Arguments { get; internal set; }
4708 /// Enables the event that has the specified identifier.
4710 /// <param name="eventId">Event ID of event to be enabled</param>
4711 /// <returns>true if eventId is in range</returns>
4712 public bool EnableEvent(int eventId)
4714 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4715 throw new InvalidOperationException();
4716 return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
4720 /// Disables the event that have the specified identifier.
4722 /// <param name="eventId">Event ID of event to be disabled</param>
4723 /// <returns>true if eventId is in range</returns>
4724 public bool DisableEvent(int eventId)
4726 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4727 throw new InvalidOperationException();
4728 return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
4733 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4734 EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4736 this.Command = command;
4737 this.Arguments = arguments;
4738 this.eventSource = eventSource;
4739 this.listener = listener;
4740 this.perEventSourceSessionId = perEventSourceSessionId;
4741 this.etwSessionId = etwSessionId;
4742 this.enable = enable;
4744 this.matchAnyKeyword = matchAnyKeyword;
4747 internal EventSource eventSource;
4748 internal EventDispatcher dispatcher;
4750 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4751 internal EventListener listener;
4752 internal int perEventSourceSessionId;
4753 internal int etwSessionId;
4754 internal bool enable;
4755 internal EventLevel level;
4756 internal EventKeywords matchAnyKeyword;
4757 internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
4763 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4765 public class EventSourceCreatedEventArgs : EventArgs
4768 /// The EventSource that is attaching to the listener.
4770 public EventSource EventSource
4778 /// EventWrittenEventArgs is passed to the user-provided override for
4779 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4781 public class EventWrittenEventArgs : EventArgs
4784 /// The name of the event.
4786 public string EventName
4790 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4795 return m_eventSource.m_eventData[EventId].Name;
4799 m_eventName = value;
4804 /// Gets the event ID for the event that was written.
4806 public int EventId { get; internal set; }
4809 /// Gets the activity ID for the thread on which the event was written.
4811 public Guid ActivityId
4813 get { return EventSource.CurrentThreadActivityId; }
4817 /// Gets the related activity ID if one was specified when the event was written.
4819 public Guid RelatedActivityId
4826 /// Gets the payload for the event.
4828 public ReadOnlyCollection<Object> Payload { get; internal set; }
4831 /// Gets the payload argument names.
4833 public ReadOnlyCollection<string> PayloadNames
4837 // For contract based events we create the list lazily.
4838 if (m_payloadNames == null)
4840 // Self described events are identified by id -1.
4841 Debug.Assert(EventId != -1);
4843 var names = new List<string>();
4844 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4846 names.Add(parameter.Name);
4848 m_payloadNames = new ReadOnlyCollection<string>(names);
4851 return m_payloadNames;
4856 m_payloadNames = value;
4861 /// Gets the event source object.
4863 public EventSource EventSource { get { return m_eventSource; } }
4866 /// Gets the keywords for the event.
4868 public EventKeywords Keywords
4872 if (EventId < 0) // TraceLogging convention EventID == -1
4875 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4880 /// Gets the operation code for the event.
4882 public EventOpcode Opcode
4886 if (EventId <= 0) // TraceLogging convention EventID == -1
4888 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4893 /// Gets the task for the event.
4895 public EventTask Task
4899 if (EventId <= 0) // TraceLogging convention EventID == -1
4900 return EventTask.None;
4902 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4907 /// Any provider/user defined options associated with the event.
4909 public EventTags Tags
4913 if (EventId <= 0) // TraceLogging convention EventID == -1
4915 return m_eventSource.m_eventData[EventId].Tags;
4920 /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
4922 public string Message
4926 if (EventId <= 0) // TraceLogging convention EventID == -1
4929 return m_eventSource.m_eventData[EventId].Message;
4938 #if FEATURE_MANAGED_ETW_CHANNELS
4940 /// Gets the channel for the event.
4942 public EventChannel Channel
4946 if (EventId <= 0) // TraceLogging convention EventID == -1
4947 return EventChannel.None;
4948 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4954 /// Gets the version of the event.
4960 if (EventId <= 0) // TraceLogging convention EventID == -1
4962 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4967 /// Gets the level for the event.
4969 public EventLevel Level
4973 if (EventId <= 0) // TraceLogging convention EventID == -1
4975 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4980 internal EventWrittenEventArgs(EventSource eventSource)
4982 m_eventSource = eventSource;
4984 private string m_message;
4985 private string m_eventName;
4986 private EventSource m_eventSource;
4987 private ReadOnlyCollection<string> m_payloadNames;
4988 internal EventTags m_tags;
4989 internal EventOpcode m_opcode;
4990 internal EventLevel m_level;
4991 internal EventKeywords m_keywords;
4996 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4998 [AttributeUsage(AttributeTargets.Class)]
4999 public sealed class EventSourceAttribute : Attribute
5002 /// Overrides the ETW name of the event source (which defaults to the class name)
5004 public string Name { get; set; }
5007 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
5008 /// except when upgrading existing ETW providers to using event sources.
5010 public string Guid { get; set; }
5014 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
5015 /// can be localized to several languages if desired. This works by creating a ResX style string table
5016 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
5017 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
5018 /// resources. This name is the value of the LocalizationResources property.
5020 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
5021 /// using the following resource naming scheme
5023 /// <para>* event_EVENTNAME</para>
5024 /// <para>* task_TASKNAME</para>
5025 /// <para>* keyword_KEYWORDNAME</para>
5026 /// <para>* map_MAPNAME</para>
5028 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
5029 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
5030 /// which represent the payload values.
5033 public string LocalizationResources { get; set; }
5037 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5038 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5039 /// name of the method and its signature to generate basic schema information for the event. The
5040 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5043 [AttributeUsage(AttributeTargets.Method)]
5044 public sealed class EventAttribute : Attribute
5046 /// <summary>Construct an EventAttribute with specified eventId</summary>
5047 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
5048 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5049 /// <summary>Event's ID</summary>
5050 public int EventId { get; private set; }
5051 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5052 public EventLevel Level { get; set; }
5053 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5054 public EventKeywords Keywords { get; set; }
5055 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5056 public EventOpcode Opcode
5064 this.m_opcode = value;
5065 this.m_opcodeSet = true;
5069 internal bool IsOpcodeSet
5077 /// <summary>Event's task: allows logical grouping of events</summary>
5078 public EventTask Task { get; set; }
5079 #if FEATURE_MANAGED_ETW_CHANNELS
5080 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5081 public EventChannel Channel { get; set; }
5083 /// <summary>Event's version</summary>
5084 public byte Version { get; set; }
5087 /// This can be specified to enable formatting and localization of the event's payload. You can
5088 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5089 /// with the 'ToString()' of the corresponding part of the event payload.
5091 public string Message { get; set; }
5094 /// User defined options associated with the event. These do not have meaning to the EventSource but
5095 /// are passed through to listeners which given them semantics.
5097 public EventTags Tags { get; set; }
5100 /// Allows fine control over the Activity IDs generated by start and stop events
5102 public EventActivityOptions ActivityOptions { get; set; }
5105 EventOpcode m_opcode;
5106 private bool m_opcodeSet;
5111 /// By default all instance methods in a class that subclasses code:EventSource that and return
5112 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5113 /// the code:NonEventAttribute
5115 [AttributeUsage(AttributeTargets.Method)]
5116 public sealed class NonEventAttribute : Attribute
5119 /// Constructs a default NonEventAttribute
5121 public NonEventAttribute() { }
5124 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5125 #if FEATURE_MANAGED_ETW_CHANNELS
5127 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5128 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5130 /// public static class Channels
5132 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5133 /// public const EventChannel Admin = (EventChannel)16;
5135 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5136 /// public const EventChannel Operational = (EventChannel)17;
5140 [AttributeUsage(AttributeTargets.Field)]
5141 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5144 class EventChannelAttribute : Attribute
5147 /// Specified whether the channel is enabled by default
5149 public bool Enabled { get; set; }
5152 /// Legal values are in EventChannelType
5154 public EventChannelType EventChannelType { get; set; }
5156 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5158 /// Specifies the isolation for the channel
5160 public EventChannelIsolation Isolation { get; set; }
5163 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5164 /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
5166 public string Access { get; set; }
5169 /// Allows importing channels defined in external manifests
5171 public string ImportChannel { get; set; }
5174 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
5175 // public string Name { get; set; }
5179 /// Allowed channel types
5181 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5184 enum EventChannelType
5186 /// <summary>The admin channel</summary>
5188 /// <summary>The operational channel</summary>
5190 /// <summary>The Analytic channel</summary>
5192 /// <summary>The debug channel</summary>
5196 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5198 /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
5199 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5200 /// access permissions for the channel and backing file.
5203 enum EventChannelIsolation
5206 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5210 /// All channels that specify System isolation use the same ETW session
5214 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5215 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5216 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5224 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5226 public enum EventCommand
5229 /// Update EventSource state
5233 /// Request EventSource to generate and send its manifest
5247 #region private classes
5249 #if FEATURE_ACTIVITYSAMPLING
5252 /// ActivityFilter is a helper structure that is used to keep track of run-time state
5253 /// associated with activity filtering. It is 1-1 with EventListeners (logically
5254 /// every listener has one of these, however we actually allocate them lazily), as well
5255 /// as 1-to-1 with tracing-aware EtwSessions.
5257 /// This structure also keeps track of the sampling counts associated with 'trigger'
5258 /// events. Because these trigger events are rare, and you typically only have one of
5259 /// them, we store them here as a linked list.
5261 internal sealed class ActivityFilter : IDisposable
5264 /// Disable all activity filtering for the listener associated with 'filterList',
5265 /// (in the session associated with it) that is triggered by any event in 'source'.
5267 public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
5269 #if !ES_BUILD_STANDALONE
5270 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5273 if (filterList == null)
5277 // Remove it from anywhere in the list (except the first element, which has to
5278 // be treated specially)
5279 ActivityFilter prev = filterList;
5283 if (cur.m_providerGuid == source.Guid)
5285 // update TriggersActivityTracking bit
5286 if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
5287 --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
5289 // Remove it from the linked list.
5290 prev.m_next = cur.m_next;
5291 // dispose of the removed node
5304 // Sadly we have to treat the first element specially in linked list removal in C#
5305 if (filterList.m_providerGuid == source.Guid)
5307 // update TriggersActivityTracking bit
5308 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
5309 --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
5311 // We are the first element in the list.
5312 var first = filterList;
5313 filterList = first.m_next;
5314 // dispose of the removed node
5317 // the above might have removed the one ActivityFilter in the session that contains the
5318 // cleanup delegate; re-create the delegate if needed
5319 if (filterList != null)
5321 EnsureActivityCleanupDelegate(filterList);
5326 /// Currently this has "override" semantics. We first disable all filters
5327 /// associated with 'source', and next we add new filters for each entry in the
5328 /// string 'startEvents'. participateInSampling specifies whether non-startEvents
5329 /// always trigger or only trigger when current activity is 'active'.
5331 public static void UpdateFilter(
5332 ref ActivityFilter filterList,
5334 int perEventSourceSessionId,
5337 #if !ES_BUILD_STANDALONE
5338 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5341 // first remove all filters associated with 'source'
5342 DisableFilter(ref filterList, source);
5344 if (!string.IsNullOrEmpty(startEvents))
5346 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
5347 // The Event may be specified by name or by ID. Errors in parsing such a pair
5348 // result in the error being reported to the listeners, and the pair being ignored.
5349 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
5350 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
5351 // we should initiate activity tracing once every 10 events.
5352 string[] activityFilterStrings = startEvents.Split(' ');
5354 for (int i = 0; i < activityFilterStrings.Length; ++i)
5356 string activityFilterString = activityFilterStrings[i];
5359 int colonIdx = activityFilterString.IndexOf(':');
5362 source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
5363 activityFilterString, false);
5364 // ignore failure...
5367 string sFreq = activityFilterString.Substring(colonIdx + 1);
5368 if (!int.TryParse(sFreq, out sampleFreq))
5370 source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
5373 activityFilterString = activityFilterString.Substring(0, colonIdx);
5374 if (!int.TryParse(activityFilterString, out eventId))
5378 // see if it's an event name
5379 for (int j = 0; j < source.m_eventData.Length; j++)
5381 EventSource.EventMetadata[] ed = source.m_eventData;
5382 if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
5383 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
5385 eventId = ed[j].Descriptor.EventId;
5390 if (eventId < 0 || eventId >= source.m_eventData.Length)
5392 source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
5395 EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
5401 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
5403 public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
5405 for (var af = filterList; af != null; af = af.m_next)
5407 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
5414 /// Returns a session mask representing all sessions in which the activity
5415 /// associated with the current thread is allowed through the activity filter.
5416 /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
5417 /// most of the time this is false as you can guarentee this event is NOT a
5418 /// triggering event. If 'triggeringEvent' is true, then it checks the
5419 /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
5420 /// a trigger. If so it activates the current activity.
5422 /// If 'childActivityID' is present, it will be added to the active set if the
5423 /// current activity is active.
5425 unsafe public static bool PassesActivityFilter(
5426 ActivityFilter filterList,
5427 Guid* childActivityID,
5428 bool triggeringEvent,
5432 Debug.Assert(filterList != null && filterList.m_activeActivities != null);
5433 bool shouldBeLogged = false;
5434 if (triggeringEvent)
5436 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5438 if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
5440 // Update the sampling count with wrap-around
5441 int curSampleCount, newSampleCount;
5444 curSampleCount = af.m_curSampleCount;
5445 if (curSampleCount <= 1)
5446 newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
5448 newSampleCount = curSampleCount - 1;
5450 while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
5451 // If we hit zero, then start tracking the activity.
5452 if (curSampleCount <= 1)
5454 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5455 Tuple<Guid, int> startId;
5456 // only add current activity if it's not already a root activity
5457 if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
5459 // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
5460 shouldBeLogged = true;
5461 af.m_activeActivities[currentActivityId] = Environment.TickCount;
5462 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
5467 // a start event following a triggering start event
5468 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5469 Tuple<Guid, int> startId;
5470 // only remove current activity if we added it
5471 if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
5472 startId.Item1 == source.Guid && startId.Item2 == eventId)
5474 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
5475 // remove activity only from current logging scope (af)
5477 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
5485 var activeActivities = GetActiveActivities(filterList);
5486 if (activeActivities != null)
5488 // if we hadn't already determined this should be logged, test further
5489 if (!shouldBeLogged)
5491 shouldBeLogged = !activeActivities.IsEmpty &&
5492 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
5494 if (shouldBeLogged && childActivityID != null &&
5495 ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
5497 FlowActivityIfNeeded(filterList, null, childActivityID);
5498 // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
5501 // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
5502 return shouldBeLogged;
5505 public static bool IsCurrentActivityActive(ActivityFilter filterList)
5507 var activeActivities = GetActiveActivities(filterList);
5508 if (activeActivities != null &&
5509 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
5516 /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
5517 /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
5518 /// value for 'currentActivityid' is an indication tha caller has already verified
5519 /// that the current activity is active.
5521 unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
5523 Debug.Assert(childActivityID != null);
5525 var activeActivities = GetActiveActivities(filterList);
5526 Debug.Assert(activeActivities != null);
5528 // take currentActivityId == null to mean we *know* the current activity is "active"
5529 if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
5532 if (activeActivities.Count > MaxActivityTrackCount)
5534 TrimActiveActivityStore(activeActivities);
5535 // make sure current activity is still in the set:
5536 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
5538 // add child activity to list of actives
5539 activeActivities[*childActivityID] = Environment.TickCount;
5545 public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
5547 for (var af = activityFilter; af != null; af = af.m_next)
5549 if ((sourceGuid == af.m_providerGuid) &&
5550 (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
5551 ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
5553 // we could be more precise here, if we tracked 'anykeywords' per session
5556 source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
5563 /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
5564 /// associated with 'this' ActivityFilter list, return configured sequence of
5565 /// [eventId, sampleFreq] pairs that defines the sampling policy.
5567 public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
5569 for (ActivityFilter af = this; af != null; af = af.m_next)
5571 if (af.m_providerGuid == sourceGuid)
5572 yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
5577 /// The cleanup being performed consists of removing the m_myActivityDelegate from
5578 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
5580 public void Dispose()
5582 #if !ES_BUILD_STANDALONE
5583 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5585 // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
5586 // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
5587 // during the finalization of the ActivityFilter
5588 if (m_myActivityDelegate != null)
5590 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
5591 m_myActivityDelegate = null;
5598 /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
5599 /// 'samplingFreq' times the event fires. You can have several of these forming a
5602 private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
5604 m_providerGuid = source.Guid;
5605 m_perEventSourceSessionId = perEventSourceSessionId;
5606 m_eventId = eventId;
5607 m_samplingFreq = samplingFreq;
5608 m_next = existingFilter;
5610 Debug.Assert(existingFilter == null ||
5611 (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
5613 // if this is the first filter we add for this session, we need to create a new
5614 // table of activities. m_activeActivities is common across EventSources in the same
5616 ConcurrentDictionary<Guid, int> activeActivities = null;
5617 if (existingFilter == null ||
5618 (activeActivities = GetActiveActivities(existingFilter)) == null)
5620 m_activeActivities = new ConcurrentDictionary<Guid, int>();
5621 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
5623 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
5624 m_myActivityDelegate = GetActivityDyingDelegate(this);
5625 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
5629 m_activeActivities = activeActivities;
5630 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
5636 /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
5637 /// activity-removing delegate for the listener/session associated with 'filterList'.
5639 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
5641 if (filterList == null)
5644 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5646 if (af.m_myActivityDelegate != null)
5650 // we didn't find a delegate
5651 filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
5652 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
5656 /// Builds the delegate to be called when an activity is dying. This is responsible
5657 /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
5658 /// This gets "added" to EventSource.s_activityDying and ends up being called from
5659 /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
5661 /// <returns>The delegate to be called when an activity is dying</returns>
5662 private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
5664 return (Guid oldActivity) =>
5667 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
5668 Tuple<Guid, int> dummyTuple;
5669 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
5674 /// Enables activity filtering for the listener associated with 'filterList', triggering on
5675 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
5677 /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
5678 /// activitySampling if something else triggered).
5680 /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
5681 private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
5683 #if !ES_BUILD_STANDALONE
5684 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5686 Debug.Assert(samplingFreq > 0);
5687 Debug.Assert(eventId >= 0);
5689 filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
5691 // Mark the 'quick Check' that indicates this is a trigger event.
5692 // If eventId is out of range then this mark is not done which has the effect of ignoring
5694 if (0 <= eventId && eventId < source.m_eventData.Length)
5695 ++source.m_eventData[eventId].TriggersActivityTracking;
5701 /// Normally this code never runs, it is here just to prevent run-away resource usage.
5703 private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
5705 if (activities.Count > MaxActivityTrackCount)
5707 // Remove half of the oldest activity ids.
5708 var keyValues = activities.ToArray();
5709 var tickNow = Environment.TickCount;
5711 // Sort by age, taking into account wrap-around. As long as x and y are within
5712 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
5713 // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
5714 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
5715 for (int i = 0; i < keyValues.Length / 2; i++)
5718 activities.TryRemove(keyValues[i].Key, out dummy);
5723 private static ConcurrentDictionary<Guid, int> GetActiveActivities(
5724 ActivityFilter filterList)
5726 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5728 if (af.m_activeActivities != null)
5729 return af.m_activeActivities;
5734 // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
5735 // in the m_next list. The 'int' value in the m_activities set is a timestamp
5736 // (Environment.TickCount) of when the entry was put in the system and is used to
5737 // remove 'old' entries that if the set gets too big.
5738 ConcurrentDictionary<Guid, int> m_activeActivities;
5740 // m_rootActiveActivities holds the "root" active activities, i.e. the activities
5741 // that were marked as active because a Start event fired on them. We need to keep
5742 // track of these to enable sampling in the scenario of an app's main thread that
5743 // never explicitly sets distinct activity IDs as it executes. To handle these
5744 // situations we manufacture a Guid from the thread's ID, and:
5745 // (a) we consider the firing of a start event when the sampling counter reaches
5746 // zero to mark the beginning of an interesting activity, and
5747 // (b) we consider the very next firing of the same start event to mark the
5748 // ending of that activity.
5749 // We use a ConcurrentDictionary to avoid taking explicit locks.
5750 // The key (a guid) represents the activity ID of the root active activity
5751 // The value is made up of the Guid of the event provider and the eventId of
5753 ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
5754 Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
5755 int m_eventId; // triggering event
5756 int m_samplingFreq; // Counter reset to this when it hits 0
5757 int m_curSampleCount; // We count down to 0 and then activate the activity.
5758 int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
5760 const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
5762 ActivityFilter m_next; // We create a linked list of these
5763 Action<Guid> m_myActivityDelegate;
5769 /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
5770 /// are limited to 8 concurrent sessions per machine (currently) we're going to store
5771 /// the active ones in a singly linked list.
5773 internal class EtwSession
5775 public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
5777 if (etwSessionId < 0)
5780 EtwSession etwSession;
5781 foreach (var wrEtwSession in s_etwSessions)
5783 #if ES_BUILD_STANDALONE
5784 if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
5787 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
5792 if (!bCreateIfNeeded)
5795 #if ES_BUILD_STANDALONE
5796 if (s_etwSessions == null)
5797 s_etwSessions = new List<WeakReference>();
5799 etwSession = new EtwSession(etwSessionId);
5800 s_etwSessions.Add(new WeakReference(etwSession));
5802 if (s_etwSessions == null)
5803 s_etwSessions = new List<WeakReference<EtwSession>>();
5805 etwSession = new EtwSession(etwSessionId);
5806 s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
5809 if (s_etwSessions.Count > s_thrSessionCount)
5816 public static void RemoveEtwSession(EtwSession etwSession)
5818 Debug.Assert(etwSession != null);
5819 if (s_etwSessions == null || etwSession == null)
5822 s_etwSessions.RemoveAll((wrEtwSession) =>
5825 #if ES_BUILD_STANDALONE
5826 return (session = (EtwSession) wrEtwSession.Target) != null &&
5827 (session.m_etwSessionId == etwSession.m_etwSessionId);
5829 return wrEtwSession.TryGetTarget(out session) &&
5830 (session.m_etwSessionId == etwSession.m_etwSessionId);
5834 if (s_etwSessions.Count > s_thrSessionCount)
5838 private static void TrimGlobalList()
5840 if (s_etwSessions == null)
5843 s_etwSessions.RemoveAll((wrEtwSession) =>
5845 #if ES_BUILD_STANDALONE
5846 return wrEtwSession.Target == null;
5849 return !wrEtwSession.TryGetTarget(out session);
5854 private EtwSession(int etwSessionId)
5856 m_etwSessionId = etwSessionId;
5859 public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
5860 public ActivityFilter m_activityFilter; // all filters enabled for this session
5862 #if ES_BUILD_STANDALONE
5863 private static List<WeakReference> s_etwSessions = new List<WeakReference>();
5865 private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
5867 private const int s_thrSessionCount = 16;
5870 #endif // FEATURE_ACTIVITYSAMPLING
5872 // holds a bitfield representing a session mask
5874 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5875 /// is the index in the SessionMask of the bit that will be set. These can translate to
5876 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5877 /// FromEventKeywords() methods.
5879 internal struct SessionMask
5881 public SessionMask(SessionMask m)
5882 { m_mask = m.m_mask; }
5884 public SessionMask(uint mask = 0)
5885 { m_mask = mask & MASK; }
5887 public bool IsEqualOrSupersetOf(SessionMask m)
5889 return (this.m_mask | m.m_mask) == this.m_mask;
5892 public static SessionMask All
5894 get { return new SessionMask(MASK); }
5897 public static SessionMask FromId(int perEventSourceSessionId)
5899 Debug.Assert(perEventSourceSessionId < MAX);
5900 return new SessionMask((uint)1 << perEventSourceSessionId);
5903 public ulong ToEventKeywords()
5905 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5908 public static SessionMask FromEventKeywords(ulong m)
5910 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5913 public bool this[int perEventSourceSessionId]
5917 Debug.Assert(perEventSourceSessionId < MAX);
5918 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5922 Debug.Assert(perEventSourceSessionId < MAX);
5923 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5924 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5928 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5930 return new SessionMask(m1.m_mask | m2.m_mask);
5933 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5935 return new SessionMask(m1.m_mask & m2.m_mask);
5938 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5940 return new SessionMask(m1.m_mask ^ m2.m_mask);
5943 public static SessionMask operator ~(SessionMask m)
5945 return new SessionMask(MASK & ~(m.m_mask));
5948 public static explicit operator ulong(SessionMask m)
5949 { return m.m_mask; }
5951 public static explicit operator uint(SessionMask m)
5952 { return m.m_mask; }
5954 private uint m_mask;
5956 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5957 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5958 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5962 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5963 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5965 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5966 /// that that EventListener has activate) and a Single EventSource may also have many
5967 /// event Dispatchers (one for every EventListener that has activated it).
5969 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5970 /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
5971 /// associated with.
5973 internal class EventDispatcher
5975 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
5978 m_EventEnabled = eventEnabled;
5979 m_Listener = listener;
5983 readonly internal EventListener m_Listener; // The dispatcher this entry is for
5984 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
5985 #if FEATURE_ACTIVITYSAMPLING
5986 internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
5987 #endif // FEATURE_ACTIVITYSAMPLING
5989 // Only guaranteed to exist after a InsureInit()
5990 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5991 // Of all listeners for that eventSource.
5995 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5999 public enum EventManifestOptions
6002 /// Only the resources associated with current UI culture are included in the manifest
6006 /// Throw exceptions for any inconsistency encountered
6010 /// Generate a "resources" node under "localization" for every satellite assembly provided
6014 /// Generate the manifest only if the event source needs to be registered on the machine,
6015 /// otherwise return null (but still perform validation if Strict is specified)
6017 OnlyIfNeededForRegistration = 0x4,
6019 /// When generating the manifest do *not* enforce the rule that the current EventSource class
6020 /// must be the base class for the user-defined type passed in. This allows validation of .net
6021 /// event sources using the new validation code
6023 AllowEventSourceOverride = 0x8,
6027 /// ManifestBuilder is designed to isolate the details of the message of the event from the
6028 /// rest of EventSource. This one happens to create XML.
6030 internal partial class ManifestBuilder
6033 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
6034 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
6036 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
6037 EventManifestOptions flags)
6039 #if FEATURE_MANAGED_ETW_CHANNELS
6040 this.providerName = providerName;
6044 this.resources = resources;
6045 sb = new StringBuilder();
6046 events = new StringBuilder();
6047 templates = new StringBuilder();
6048 opcodeTab = new Dictionary<int, string>();
6049 stringTab = new Dictionary<string, string>();
6050 errors = new List<string>();
6051 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
6053 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6054 sb.AppendLine(" <instrumentation xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\">");
6055 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6056 sb.Append("<provider name=\"").Append(providerName).
6057 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
6058 if (dllName != null)
6059 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
6061 var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
6062 sb.Append("\" symbol=\"").Append(symbolsName);
6063 sb.Append("\">").AppendLine();
6066 public void AddOpcode(string name, int value)
6068 if ((flags & EventManifestOptions.Strict) != 0)
6070 if (value <= 10 || value >= 239)
6072 ManifestError(Resources.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
6075 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6077 ManifestError(Resources.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
6080 opcodeTab[value] = name;
6082 public void AddTask(string name, int value)
6084 if ((flags & EventManifestOptions.Strict) != 0)
6086 if (value <= 0 || value >= 65535)
6088 ManifestError(Resources.GetResourceString("EventSource_IllegalTaskValue", name, value));
6091 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6093 ManifestError(Resources.GetResourceString("EventSource_TaskCollision", name, prevName, value));
6096 if (taskTab == null)
6097 taskTab = new Dictionary<int, string>();
6098 taskTab[value] = name;
6100 public void AddKeyword(string name, ulong value)
6102 if ((value & (value - 1)) != 0) // Is it a power of 2?
6104 ManifestError(Resources.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
6106 if ((flags & EventManifestOptions.Strict) != 0)
6108 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
6110 ManifestError(Resources.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6113 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6115 ManifestError(Resources.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6118 if (keywordTab == null)
6119 keywordTab = new Dictionary<ulong, string>();
6120 keywordTab[value] = name;
6123 #if FEATURE_MANAGED_ETW_CHANNELS
6125 /// Add a channel. channelAttribute can be null
6127 public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
6129 EventChannel chValue = (EventChannel)value;
6130 if (value < (int)EventChannel.Admin || value > 255)
6131 ManifestError(Resources.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
6132 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
6133 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
6135 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
6136 // but we want to allow them to override the default ones...
6137 ManifestError(Resources.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
6138 name, ((EventChannel)value).ToString()));
6141 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
6143 ulong kwd = GetChannelKeyword(chValue);
6145 if (channelTab == null)
6146 channelTab = new Dictionary<int, ChannelInfo>(4);
6147 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
6150 private EventChannelType EventChannelToChannelType(EventChannel channel)
6152 #if !ES_BUILD_STANDALONE
6153 Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
6155 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
6157 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
6159 EventChannelAttribute attrib = new EventChannelAttribute();
6160 attrib.EventChannelType = EventChannelToChannelType(channel);
6161 if (attrib.EventChannelType <= EventChannelType.Operational)
6162 attrib.Enabled = true;
6166 public ulong[] GetChannelData()
6168 if (this.channelTab == null)
6170 return new ulong[0];
6173 // We create an array indexed by the channel id for fast look up.
6174 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
6176 foreach (var item in this.channelTab.Keys)
6184 ulong[] channelMask = new ulong[maxkey + 1];
6185 foreach (var item in this.channelTab)
6187 channelMask[item.Key] = item.Value.Keywords;
6194 public void StartEvent(string eventName, EventAttribute eventAttribute)
6196 Debug.Assert(numParams == 0);
6197 Debug.Assert(this.eventName == null);
6198 this.eventName = eventName;
6200 byteArrArgIndices = null;
6202 events.Append(" <event").
6203 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
6204 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
6205 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
6206 Append(" symbol=\"").Append(eventName).Append("\"");
6208 // at this point we add to the manifest's stringTab a message that is as-of-yet
6209 // "untranslated to manifest convention", b/c we don't have the number or position
6210 // of any byte[] args (which require string format index updates)
6211 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
6213 if (eventAttribute.Keywords != 0)
6214 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
6215 if (eventAttribute.Opcode != 0)
6216 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
6217 if (eventAttribute.Task != 0)
6218 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
6219 #if FEATURE_MANAGED_ETW_CHANNELS
6220 if (eventAttribute.Channel != 0)
6222 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
6227 public void AddEventParameter(Type type, string name)
6230 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
6231 if (type == typeof(byte[]))
6233 // mark this index as "extraneous" (it has no parallel in the managed signature)
6234 // we use these values in TranslateToManifestConvention()
6235 if (byteArrArgIndices == null)
6236 byteArrArgIndices = new List<int>(4);
6237 byteArrArgIndices.Add(numParams);
6239 // add an extra field to the template representing the length of the binary blob
6241 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
6244 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
6245 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
6246 // as for 'byte[]' args (blob_arg_name + "Size")
6247 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6249 // add "length" attribute to the "blob" field in the template (referencing the field added above)
6250 templates.Append(" length=\"").Append(name).Append("Size\"");
6252 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
6253 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
6255 templates.Append(" map=\"").Append(type.Name).Append("\"");
6256 if (mapsTab == null)
6257 mapsTab = new Dictionary<string, Type>();
6258 if (!mapsTab.ContainsKey(type.Name))
6259 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
6262 templates.Append("/>").AppendLine();
6264 public void EndEvent()
6268 templates.Append(" </template>").AppendLine();
6269 events.Append(" template=\"").Append(eventName).Append("Args\"");
6271 events.Append("/>").AppendLine();
6273 if (byteArrArgIndices != null)
6274 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
6276 // at this point we have all the information we need to translate the C# Message
6277 // to the manifest string we'll put in the stringTab
6279 if (stringTab.TryGetValue("event_" + eventName, out msg))
6281 msg = TranslateToManifestConvention(msg, eventName);
6282 stringTab["event_" + eventName] = msg;
6287 byteArrArgIndices = null;
6290 #if FEATURE_MANAGED_ETW_CHANNELS
6291 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
6292 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
6293 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
6294 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
6295 // to channels by the OS infrastructure).
6296 // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
6297 // assumed that that the keyword for that channel should be that bit.
6298 // otherwise we allocate a channel bit for the channel.
6299 // explicit channel bits are only used by WCF to mimic an existing manifest,
6300 // so we don't dont do error checking.
6301 public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
6303 // strip off any non-channel keywords, since we are only interested in channels here.
6304 channelKeyword &= ValidPredefinedChannelKeywords;
6305 if (channelTab == null)
6307 channelTab = new Dictionary<int, ChannelInfo>(4);
6310 if (channelTab.Count == MaxCountChannels)
6311 ManifestError(Resources.GetResourceString("EventSource_MaxChannelExceeded"));
6314 if (!channelTab.TryGetValue((int)channel, out info))
6316 // If we were not given an explicit channel, allocate one.
6317 if (channelKeyword != 0)
6319 channelKeyword = nextChannelKeywordBit;
6320 nextChannelKeywordBit >>= 1;
6325 channelKeyword = info.Keywords;
6328 return channelKeyword;
6332 public byte[] CreateManifest()
6334 string str = CreateManifestString();
6335 return Encoding.UTF8.GetBytes(str);
6338 public IList<string> Errors { get { return errors; } }
6341 /// When validating an event source it adds the error to the error collection.
6342 /// When not validating it throws an exception if runtimeCritical is "true".
6343 /// Otherwise the error is ignored.
6345 /// <param name="msg"></param>
6346 /// <param name="runtimeCritical"></param>
6347 public void ManifestError(string msg, bool runtimeCritical = false)
6349 if ((flags & EventManifestOptions.Strict) != 0)
6351 else if (runtimeCritical)
6352 throw new ArgumentException(msg);
6355 private string CreateManifestString()
6358 #if FEATURE_MANAGED_ETW_CHANNELS
6359 // Write out the channels
6360 if (channelTab != null)
6362 sb.Append(" <channels>").AppendLine();
6363 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
6364 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
6365 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
6366 foreach (var kvpair in sortedChannels)
6368 int channel = kvpair.Key;
6369 ChannelInfo channelInfo = kvpair.Value;
6371 string channelType = null;
6372 string elementName = "channel";
6373 bool enabled = false;
6374 string fullName = null;
6375 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6376 string isolation = null;
6377 string access = null;
6379 if (channelInfo.Attribs != null)
6381 var attribs = channelInfo.Attribs;
6382 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
6383 channelType = attribs.EventChannelType.ToString();
6384 enabled = attribs.Enabled;
6385 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6386 if (attribs.ImportChannel != null)
6388 fullName = attribs.ImportChannel;
6389 elementName = "importChannel";
6391 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
6392 isolation = attribs.Isolation.ToString();
6393 access = attribs.Access;
6396 if (fullName == null)
6397 fullName = providerName + "/" + channelInfo.Name;
6399 sb.Append(" <").Append(elementName);
6400 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
6401 sb.Append(" name=\"").Append(fullName).Append("\"");
6402 if (elementName == "channel") // not applicable to importChannels.
6404 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
6405 sb.Append(" value=\"").Append(channel).Append("\"");
6406 if (channelType != null)
6407 sb.Append(" type=\"").Append(channelType).Append("\"");
6408 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
6409 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6411 sb.Append(" access=\"").Append(access).Append("\"");
6412 if (isolation != null)
6413 sb.Append(" isolation=\"").Append(isolation).Append("\"");
6416 sb.Append("/>").AppendLine();
6418 sb.Append(" </channels>").AppendLine();
6422 // Write out the tasks
6423 if (taskTab != null)
6426 sb.Append(" <tasks>").AppendLine();
6427 var sortedTasks = new List<int>(taskTab.Keys);
6429 foreach (int task in sortedTasks)
6431 sb.Append(" <task");
6432 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
6433 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
6435 sb.Append(" </tasks>").AppendLine();
6438 // Write out the maps
6439 if (mapsTab != null)
6441 sb.Append(" <maps>").AppendLine();
6442 foreach (Type enumType in mapsTab.Values)
6444 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
6445 string mapKind = isbitmap ? "bitMap" : "valueMap";
6446 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
6448 // write out each enum value
6449 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
6450 foreach (FieldInfo staticField in staticFields)
6452 object constantValObj = staticField.GetRawConstantValue();
6453 if (constantValObj != null)
6456 if (constantValObj is int)
6457 hexValue = ((int)constantValObj);
6458 else if (constantValObj is long)
6459 hexValue = ((long)constantValObj);
6463 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
6464 // TODO: Warn people about the dropping of values.
6465 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
6468 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
6469 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
6470 sb.Append("/>").AppendLine();
6473 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
6475 sb.Append(" </maps>").AppendLine();
6478 // Write out the opcodes
6479 sb.Append(" <opcodes>").AppendLine();
6480 var sortedOpcodes = new List<int>(opcodeTab.Keys);
6481 sortedOpcodes.Sort();
6482 foreach (int opcode in sortedOpcodes)
6484 sb.Append(" <opcode");
6485 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
6486 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
6488 sb.Append(" </opcodes>").AppendLine();
6490 // Write out the keywords
6491 if (keywordTab != null)
6493 sb.Append(" <keywords>").AppendLine();
6494 var sortedKeywords = new List<ulong>(keywordTab.Keys);
6495 sortedKeywords.Sort();
6496 foreach (ulong keyword in sortedKeywords)
6498 sb.Append(" <keyword");
6499 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
6500 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
6502 sb.Append(" </keywords>").AppendLine();
6505 sb.Append(" <events>").AppendLine();
6507 sb.Append(" </events>").AppendLine();
6509 sb.Append(" <templates>").AppendLine();
6510 if (templates.Length > 0)
6512 sb.Append(templates);
6516 // Work around a cornercase ETW issue where a manifest with no templates causes
6517 // ETW events to not get sent to their associated channel.
6518 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
6520 sb.Append(" </templates>").AppendLine();
6522 sb.Append("</provider>").AppendLine();
6523 sb.Append("</events>").AppendLine();
6524 sb.Append("</instrumentation>").AppendLine();
6526 // Output the localization information.
6527 sb.Append("<localization>").AppendLine();
6529 List<CultureInfo> cultures = null;
6530 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
6532 cultures = GetSupportedCultures(resources);
6536 cultures = new List<CultureInfo>();
6537 cultures.Add(CultureInfo.CurrentUICulture);
6539 #if ES_BUILD_STANDALONE || ES_BUILD_PN
6540 var sortedStrings = new List<string>(stringTab.Keys);
6541 sortedStrings.Sort();
6544 var sortedStrings = new string[stringTab.Keys.Count];
6545 stringTab.Keys.CopyTo(sortedStrings, 0);
6546 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
6547 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
6548 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
6549 // access BinaryCompatibility.
6550 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, string.Compare);
6552 foreach (var ci in cultures)
6554 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
6555 sb.Append(" <stringTable>").AppendLine();
6557 foreach (var stringKey in sortedStrings)
6559 string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
6560 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
6562 sb.Append(" </stringTable>").AppendLine();
6563 sb.Append(" </resources>").AppendLine();
6565 sb.Append("</localization>").AppendLine();
6566 sb.AppendLine("</instrumentationManifest>");
6567 return sb.ToString();
6571 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
6573 stringBuilder.Append(" name=\"").Append(name).Append("\"");
6574 WriteMessageAttrib(sb, elementName, name, name);
6576 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
6578 string key = elementName + "_" + name;
6579 // See if the user wants things localized.
6580 if (resources != null)
6582 // resource fallback: strings in the neutral culture will take precedence over inline strings
6583 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
6584 if (localizedString != null)
6585 value = localizedString;
6590 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
6592 if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
6594 ManifestError(Resources.GetResourceString("EventSource_DuplicateStringKey", key), true);
6598 stringTab[key] = value;
6600 internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
6602 string value = null;
6603 if (resources != null)
6605 string localizedString = resources.GetString(key, ci);
6606 if (localizedString != null)
6608 value = localizedString;
6609 if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
6611 var evtName = key.Substring("event_".Length);
6612 value = TranslateToManifestConvention(value, evtName);
6616 if (etwFormat && value == null)
6617 stringTab.TryGetValue(key, out value);
6623 /// There's no API to enumerate all languages an assembly is localized into, so instead
6624 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
6627 /// <param name="resources"></param>
6628 /// <returns></returns>
6629 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
6631 var cultures = new List<CultureInfo>();
6633 if (!cultures.Contains(CultureInfo.CurrentUICulture))
6634 cultures.Insert(0, CultureInfo.CurrentUICulture);
6638 private static string GetLevelName(EventLevel level)
6640 return (((int)level >= 16) ? "" : "win:") + level.ToString();
6643 #if FEATURE_MANAGED_ETW_CHANNELS
6644 private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
6646 ChannelInfo info = null;
6647 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6649 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6650 ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6652 // allow channels to be auto-defined. The well known ones get their well known names, and the
6653 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
6654 if (channelTab == null)
6655 channelTab = new Dictionary<int, ChannelInfo>(4);
6657 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
6658 if (EventChannel.Debug < channel)
6659 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
6661 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6662 if (!channelTab.TryGetValue((int)channel, out info))
6663 ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6665 // events that specify admin channels *must* have non-null "Message" attributes
6666 if (resources != null && eventMessage == null)
6667 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6668 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6669 ManifestError(Resources.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
6673 private string GetTaskName(EventTask task, string eventName)
6675 if (task == EventTask.None)
6679 if (taskTab == null)
6680 taskTab = new Dictionary<int, string>();
6681 if (!taskTab.TryGetValue((int)task, out ret))
6682 ret = taskTab[(int)task] = eventName;
6686 private string GetOpcodeName(EventOpcode opcode, string eventName)
6690 case EventOpcode.Info:
6692 case EventOpcode.Start:
6694 case EventOpcode.Stop:
6696 case EventOpcode.DataCollectionStart:
6697 return "win:DC_Start";
6698 case EventOpcode.DataCollectionStop:
6699 return "win:DC_Stop";
6700 case EventOpcode.Extension:
6701 return "win:Extension";
6702 case EventOpcode.Reply:
6704 case EventOpcode.Resume:
6705 return "win:Resume";
6706 case EventOpcode.Suspend:
6707 return "win:Suspend";
6708 case EventOpcode.Send:
6710 case EventOpcode.Receive:
6711 return "win:Receive";
6715 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6717 ManifestError(Resources.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
6723 private string GetKeywords(ulong keywords, string eventName)
6725 #if FEATURE_MANAGED_ETW_CHANNELS
6726 // ignore keywords associate with channels
6727 // See ValidPredefinedChannelKeywords def for more.
6728 keywords &= ~ValidPredefinedChannelKeywords;
6732 for (ulong bit = 1; bit != 0; bit <<= 1)
6734 if ((keywords & bit) != 0)
6736 string keyword = null;
6737 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6738 (bit >= (ulong)0x1000000000000))
6740 // do not report Windows reserved keywords in the manifest (this allows the code
6741 // to be resilient to potential renaming of these keywords)
6742 keyword = string.Empty;
6744 if (keyword == null)
6746 ManifestError(Resources.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6747 keyword = string.Empty;
6749 if (ret.Length != 0 && keyword.Length != 0)
6751 ret = ret + keyword;
6757 private string GetTypeName(Type type)
6761 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6762 var typeName = GetTypeName(fields[0].FieldType);
6763 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6766 return GetTypeNameHelper(type);
6769 private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
6771 if (stringBuilder == null)
6772 stringBuilder = new StringBuilder();
6773 stringBuilder.Append(eventMessage, startIndex, count);
6776 private static readonly string[] s_escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" };
6777 // Manifest messages use %N conventions for their message substitutions. Translate from
6778 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6779 private string TranslateToManifestConvention(string eventMessage, string evtName)
6781 StringBuilder stringBuilder = null; // We lazily create this
6782 int writtenSoFar = 0;
6786 if (i >= eventMessage.Length)
6788 if (stringBuilder == null)
6789 return eventMessage;
6790 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6791 return stringBuilder.ToString();
6794 if (eventMessage[i] == '%')
6796 // handle format message escaping character '%' by escaping it
6797 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6798 stringBuilder.Append("%%");
6802 else if (i < eventMessage.Length - 1 &&
6803 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6805 // handle C# escaped '{" and '}'
6806 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6807 stringBuilder.Append(eventMessage[i]);
6811 else if (eventMessage[i] == '{')
6813 int leftBracket = i;
6816 while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
6818 argNum = argNum * 10 + eventMessage[i] - '0';
6821 if (i < eventMessage.Length && eventMessage[i] == '}')
6824 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6825 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6826 stringBuilder.Append('%').Append(manIndex);
6827 // An '!' after the insert specifier {n} will be interpreted as a literal.
6828 // We'll escape it so that mc.exe does not attempt to consider it the
6829 // beginning of a format string.
6830 if (i < eventMessage.Length && eventMessage[i] == '!')
6833 stringBuilder.Append("%!");
6839 ManifestError(Resources.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
6842 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6844 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6846 stringBuilder.Append(s_escapes[chIdx]);
6854 private int TranslateIndexToManifestConvention(int idx, string evtName)
6856 List<int> byteArrArgIndices;
6857 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6859 foreach (var byArrIdx in byteArrArgIndices)
6861 if (idx >= byArrIdx)
6870 #if FEATURE_MANAGED_ETW_CHANNELS
6874 public ulong Keywords;
6875 public EventChannelAttribute Attribs;
6879 Dictionary<int, string> opcodeTab;
6880 Dictionary<int, string> taskTab;
6881 #if FEATURE_MANAGED_ETW_CHANNELS
6882 Dictionary<int, ChannelInfo> channelTab;
6884 Dictionary<ulong, string> keywordTab;
6885 Dictionary<string, Type> mapsTab;
6887 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6889 #if FEATURE_MANAGED_ETW_CHANNELS
6890 // WCF used EventSource to mimic a existing ETW manifest. To support this
6891 // in just their case, we allowed them to specify the keywords associated
6892 // with their channels explicitly. ValidPredefinedChannelKeywords is
6893 // this set of channel keywords that we allow to be explicitly set. You
6894 // can ignore these bits otherwise.
6895 internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
6896 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
6897 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6900 StringBuilder sb; // Holds the provider information.
6901 StringBuilder events; // Holds the events.
6902 StringBuilder templates;
6904 #if FEATURE_MANAGED_ETW_CHANNELS
6905 string providerName;
6907 ResourceManager resources; // Look up localized strings here.
6908 EventManifestOptions flags;
6909 IList<string> errors; // list of currently encountered errors
6910 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6912 // State we track between StartEvent and EndEvent.
6913 string eventName; // Name of the event currently being processed.
6914 int numParams; // keeps track of the number of args the event has.
6915 List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
6920 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6922 internal struct ManifestEnvelope
6924 public const int MaxChunkSize = 0xFF00;
6925 public enum ManifestFormats : byte
6927 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6930 #if FEATURE_MANAGED_ETW
6931 public ManifestFormats Format;
6932 public byte MajorVersion;
6933 public byte MinorVersion;
6935 public ushort TotalChunks;
6936 public ushort ChunkNumber;