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
7 #if !ES_BUILD_STANDALONE && !CORECLR && !ES_BUILD_PN
8 #define FEATURE_ACTIVITYSAMPLING
9 #endif // !ES_BUILD_STANDALONE
11 #if ES_BUILD_STANDALONE
12 #define FEATURE_MANAGED_ETW_CHANNELS
13 // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
16 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
18 // Over the years EventSource has become more complex and so it is important to understand
19 // the basic structure of the code to insure that it does not grow more complex.
23 // PRINCIPLE: EventSource - ETW decoupling
25 // Conceptually and EventSouce is something takes event logging data from the source methods
26 // To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
27 // KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
28 // we will call the EtwEventListener, that forwards commands from ETW to EventSources and
29 // listeners to the EventSources and forwards on those events to ETW. THus the model should
30 // be that you DON'T NEED ETW.
32 // Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
33 // to it directly, but this can be VIEWED AS AN OPTIMIATION.
35 // Basic Event Data Flow:
37 // There are two ways for event Data to enter the system
38 // 1) WriteEvent* and friends. This is called the 'contract' based approach because
39 // you write a method per event which forms a contract that is know at compile time.
40 // In this scheme each event is given an EVENTID (small integer). which is its identity
41 // 2) Write<T> methods. This is called the 'dynamic' approach because new events
42 // can be created on the fly. Event identity is determined by the event NAME, and these
43 // are not quite as efficient at runtime since you have at least a hash table lookup
44 // on every event write.
46 // EventSource-EventListener transfer fully support both ways of writing events (either contract
47 // based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
48 // types. It is suggested, however, that you use the contract based approach when the event scheme
49 // is known at compile time (that is whenever possible). It is more efficient, but more importantly
50 // it makes the contract very explicit, and centralizes all policy about logging. These are good
51 // things. The Write<T> API is really meant for more ad-hoc
55 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
56 // during the transfer. In particular object identity is not preserved, some objects are morphed,
57 // and not all data types are supported. In particular you can pass
59 // A Valid type to log to an EventSource include
60 // * Primitive data types
61 // * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
62 // * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
64 // This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
66 // Explicitly allowed structs include (* New for V4.6)
67 // * Marked with the EventData attribute
68 // * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
69 // * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
71 // When classes are returned in an EventListener, what is returned is something that implements
72 // IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
73 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
74 // are obvious NOT the original objects.
76 // ETWserialization formats:
78 // As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
79 // copy/morph of that data as described above. In addition the .NET framework supports a conceptual
80 // ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
81 // to be serialized in a way that ETW supports. ETW supports the following serialization formats
83 // 1) Manifest Based serialization.
84 // 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
86 // A key factor is that the Write<T> method, which support on the fly definition of events, can't
87 // support the manifest based serialization because the manifest needs the schema of all events
88 // to be known before any events are emitted. This implies the following
90 // If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
91 // If you use the EventSource(string) constructor for an eventSource (in which you don't
92 // create a subclass), the default is also to use Self-Describing serialization. In addition
93 // you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
94 // Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
96 // Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
98 // *************************************************************************************
99 // *** INTERNALS: Event Propagation
101 // Data enters the system either though
103 // 1) A user defined method in the user defined subclass of EventSource which calls
104 // A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
105 // * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
106 // B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
107 // C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
109 // All event data eventually flows to one of
110 // * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
111 // * WriteEventVarargs(ID, Guid*, object[])
113 // 2) A call to one of the overloads of Write<T>. All these overloads end up in
114 // * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
116 // On output there are the following routines
117 // Writing to all listeners that are NOT ETW, we have the following routines
118 // * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
119 // * WriteToAllListeners(ID, Guid*, object[])
120 // * WriteToAllListeners(NAME, Guid*, EventPayload)
122 // EventPayload is the internal type that implements the IDictionary<string, object> interface
123 // The EventListeners will pass back for serialized classes for nested object, but
124 // WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
125 // were parameters to a method.
127 // The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
129 // Writing to ETW, Manifest Based
130 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
131 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
132 // Writing to ETW, Self-Describing format
133 // WriteMultiMerge(NAME, Options, Types, EventData*)
134 // WriteMultiMerge(NAME, Options, Types, object[])
135 // WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
138 // All ETW writes eventually call
139 // EventWriteTransfer (native PINVOKE wrapper)
140 // EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
141 // EventProvider.WriteEventRaw - sets last error
142 // EventSource.WriteEventRaw - Does EventSource exception handling logic
145 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
146 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
148 // Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
149 // how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
150 // since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
151 // can call one of these
152 // WriteMetadata - transforms the type T into serialization meta data blob for that type
153 // WriteObjectData - transforms an object of T into serialization meta data blob for that type
154 // GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
155 // The first two are used to serialize something for ETW. The second one is used to transform the object
156 // for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
157 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
159 // It is an important observation that while EventSource does support users directly calling with EventData*
160 // blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
161 // path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
163 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
165 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
166 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
167 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
168 // This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
169 // opportunity to expose this format to EventListeners in the future.
172 using System.Runtime.CompilerServices;
173 #if FEATURE_ACTIVITYSAMPLING
174 using System.Collections.Concurrent;
176 using System.Collections.Generic;
177 using System.Collections.ObjectModel;
178 using System.Diagnostics;
179 using System.Diagnostics.CodeAnalysis;
180 using System.Globalization;
181 using System.Reflection;
182 using System.Resources;
183 using System.Security;
184 #if !CORECLR && !ES_BUILD_PN
185 using System.Security.Permissions;
186 #endif // !CORECLR && !ES_BUILD_PN
189 using System.Threading;
190 using Microsoft.Win32;
192 #if ES_BUILD_STANDALONE
193 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
195 using System.Threading.Tasks;
198 using Microsoft.Reflection;
200 #if !ES_BUILD_AGAINST_DOTNET_V35
201 using Contract = System.Diagnostics.Contracts.Contract;
203 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
206 #if CORECLR || ES_BUILD_PN
207 using Internal.Runtime.Augments;
210 #if ES_BUILD_STANDALONE
211 namespace Microsoft.Diagnostics.Tracing
213 namespace System.Diagnostics.Tracing
217 /// This class is meant to be inherited by a user-defined event source in order to define a managed
218 /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
219 /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
220 /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
221 /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
222 /// is sufficient for many users.
224 /// To achieve more control over the ETW provider manifest exposed by the event source type, the
225 /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
227 /// For very advanced EventSources, it is possible to intercept the commands being given to the
228 /// eventSource and change what filtering is done (see EventListener.EnableEvents and
229 /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
230 /// e.g. dumping a data structure (see EventSource.SendCommand and
231 /// <see cref="EventSource.OnEventCommand"/>).
233 /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
234 /// It is also possible to control and intercept the data dispatcher programmatically. See
235 /// <see cref="EventListener"/> for more.
239 /// This is a minimal definition for a custom event source:
241 /// [EventSource(Name="Samples-Demos-Minimal")]
242 /// sealed class MinimalEventSource : EventSource
244 /// public static MinimalEventSource Log = new MinimalEventSource();
245 /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
246 /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
247 /// private MinimalEventSource() {}
251 public partial class EventSource : IDisposable
254 #if FEATURE_EVENTSOURCE_XPLAT
255 private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
256 #endif //FEATURE_EVENTSOURCE_XPLAT
259 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
261 public string Name { get { return m_name; } }
263 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
265 public Guid Guid { get { return m_guid; } }
268 /// Returns true if the eventSource has been enabled at all. This is the prefered test
269 /// to be performed before a relatively expensive EventSource operation.
271 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
272 public bool IsEnabled()
274 return m_eventSourceEnabled;
278 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
280 /// Note that the result of this function is only an approximation on whether a particular
281 /// event is active or not. It is only meant to be used as way of avoiding expensive
282 /// computation for logging when logging is not on, therefore it sometimes returns false
283 /// positives (but is always accurate when returning false). EventSources are free to
284 /// have additional filtering.
286 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
287 public bool IsEnabled(EventLevel level, EventKeywords keywords)
289 return IsEnabled(level, keywords, EventChannel.None);
293 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
294 /// if 'keywords' specifies a channel bit for a channel that is enabled.
296 /// Note that the result of this function only an approximation on whether a particular
297 /// event is active or not. It is only meant to be used as way of avoiding expensive
298 /// computation for logging when logging is not on, therefore it sometimes returns false
299 /// positives (but is always accurate when returning false). EventSources are free to
300 /// have additional filtering.
302 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
303 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
305 if (!m_eventSourceEnabled)
308 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
311 #if !FEATURE_ACTIVITYSAMPLING
315 #else // FEATURE_ACTIVITYSAMPLING
319 #if OPTIMIZE_IS_ENABLED
320 //================================================================================
321 // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
322 // in case activity tracing/sampling is enabled. The added complexity of this
323 // code however weighs against having it "on" until we know it's really needed.
324 // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
325 //================================================================================
327 // At this point we believe the event is enabled, however we now need to check
328 // if we filter because of activity
330 // Optimization, all activity filters also register a delegate here, so if there
331 // is no delegate, we know there are no activity filters, which means that there
332 // is no additional filtering, which means that we can return true immediately.
333 if (s_activityDying == null)
336 // if there's at least one legacy ETW listener we can't filter this
337 if (m_legacySessions != null && m_legacySessions.Count > 0)
340 // if any event ID that triggers a new activity, or "transfers" activities
341 // is covered by 'keywords' we can't filter this
342 if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
345 // See if all listeners have activity filters that would block the event.
346 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
348 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
349 if (etwSession == null)
352 ActivityFilter activityFilter = etwSession.m_activityFilter;
353 if (activityFilter == null ||
354 ActivityFilter.GetFilter(activityFilter, this) == null)
356 // No activity filter for ETW, if event is active for ETW, we can't filter.
357 for (int i = 0; i < m_eventData.Length; i++)
358 if (m_eventData[i].EnabledForETW)
361 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
365 // for regular event listeners
366 var curDispatcher = m_Dispatchers;
367 while (curDispatcher != null)
369 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
370 if (activityFilter == null)
372 // See if any event is enabled.
373 for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
374 if (curDispatcher.m_EventEnabled[i])
377 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
379 curDispatcher = curDispatcher.m_Next;
382 // Every listener has an activity filter that is blocking writing the event,
383 // thus the event is not enabled.
385 #endif // OPTIMIZE_IS_ENABLED
387 #endif // FEATURE_ACTIVITYSAMPLING
391 /// Returns the settings for the event source instance
393 public EventSourceSettings Settings
395 get { return m_config; }
400 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
401 /// This API allows you to compute this without actually creating an instance of the EventSource.
402 /// It only needs to reflect over the type.
404 public static Guid GetGuid(Type eventSourceType)
406 if (eventSourceType == null)
407 throw new ArgumentNullException(nameof(eventSourceType));
408 Contract.EndContractBlock();
410 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
411 string name = eventSourceType.Name;
414 if (attrib.Guid != null)
417 #if !ES_BUILD_AGAINST_DOTNET_V35
418 if (Guid.TryParse(attrib.Guid, out g))
421 try { return new Guid(attrib.Guid); }
422 catch (Exception) { }
426 if (attrib.Name != null)
432 throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), nameof(eventSourceType));
434 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
437 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
438 /// This API allows you to compute this without actually creating an instance of the EventSource.
439 /// It only needs to reflect over the type.
441 public static string GetName(Type eventSourceType)
443 return GetName(eventSourceType, EventManifestOptions.None);
447 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
448 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
449 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
450 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
452 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
453 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
454 /// which it is embedded. This parameter specifies what name will be used</param>
455 /// <returns>The XML data string</returns>
456 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
458 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
461 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
462 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
463 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
464 /// ensures that the entries in the event log will be "optimally" localized.
466 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
467 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
468 /// which it is embedded. This parameter specifies what name will be used</param>
469 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
470 /// this returns null when the eventSourceType does not require explicit registration</param>
471 /// <returns>The XML data string or null</returns>
472 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
474 if (eventSourceType == null)
475 throw new ArgumentNullException(nameof(eventSourceType));
476 Contract.EndContractBlock();
478 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
479 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
482 // EventListener support
484 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
486 /// <returns></returns>
487 public static IEnumerable<EventSource> GetSources()
489 var ret = new List<EventSource>();
490 lock (EventListener.EventListenersLock)
492 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
494 EventSource eventSource = eventSourceRef.Target as EventSource;
495 if (eventSource != null && !eventSource.IsDisposed)
496 ret.Add(eventSource);
503 /// Send a command to a particular EventSource identified by 'eventSource'.
504 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
505 /// callback. What the EventSource does with the command and its arguments are from
506 /// that point EventSource-specific.
508 /// <param name="eventSource">The instance of EventSource to send the command to</param>
509 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
510 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
511 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
513 if (eventSource == null)
514 throw new ArgumentNullException(nameof(eventSource));
516 // User-defined EventCommands should not conflict with the reserved commands.
517 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
519 throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), nameof(command));
522 eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
525 #if !ES_BUILD_STANDALONE
527 /// This property allows EventSource code to appropriately handle as "different"
528 /// activities started on different threads that have not had an activity created on them.
530 internal static Guid InternalCurrentThreadActivityId
534 Guid retval = CurrentThreadActivityId;
535 if (retval == Guid.Empty)
537 retval = FallbackActivityId;
543 internal static Guid FallbackActivityId
547 #pragma warning disable 612, 618
548 int threadID = AppDomain.GetCurrentThreadId();
550 // Managed thread IDs are more aggressively re-used than native thread IDs,
551 // so we'll use the latter...
552 return new Guid(unchecked((uint)threadID),
553 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
554 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
555 #pragma warning restore 612, 618
558 #endif // !ES_BUILD_STANDALONE
560 // Error APIs. (We don't throw by default, but you can probe for status)
564 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
565 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
567 /// The event source constructor does not throw exceptions. Instead we remember any exception that
568 /// was generated (it is also logged to Trace.WriteLine).
570 public Exception ConstructionException { get { return m_constructionException; } }
573 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
574 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
575 /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
576 /// construction time and can be retrieved by using this GetTrait API.
578 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
579 /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
580 public string GetTrait(string key)
582 if (m_traits != null)
584 for (int i = 0; i < m_traits.Length - 1; i += 2)
586 if (m_traits[i] == key)
587 return m_traits[i + 1];
594 /// Displays the name and GUID for the eventSource for debugging purposes.
596 public override string ToString()
598 return Resources.GetResourceString("EventSource_ToString", Name, Guid);
602 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
604 public event EventHandler<EventCommandEventArgs> EventCommandExecuted
608 m_eventCommandExecuted += value;
610 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
611 // It should get a chance to handle the deferred commands.
612 EventCommandEventArgs deferredCommands = m_deferredCommands;
613 while (deferredCommands != null)
615 value(this, deferredCommands);
616 deferredCommands = deferredCommands.nextCommand;
621 m_eventCommandExecuted -= value;
627 /// This is the constructor that most users will use to create their eventSource. It takes
628 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
629 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
630 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
631 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
632 /// the ETW provider name.
634 protected EventSource()
635 : this(EventSourceSettings.EtwManifestEventFormat)
640 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
641 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
642 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
643 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
644 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
645 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
647 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
649 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
650 protected EventSource(bool throwOnEventWriteErrors)
651 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
655 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
657 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
660 /// Construct an EventSource with additional non-default settings.
662 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
663 /// The first string is the key and the second is the value. These are not interpreted by EventSource
664 /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
666 /// <param name="settings">See EventSourceSettings for more.</param>
667 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
668 protected EventSource(EventSourceSettings settings, params string[] traits)
670 m_config = ValidateSettings(settings);
672 Guid eventSourceGuid;
673 string eventSourceName;
675 EventMetadata[] eventDescriptors;
677 GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
679 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
681 var myType = this.GetType();
682 eventSourceGuid = GetGuid(myType);
683 eventSourceName = GetName(myType);
686 Initialize(eventSourceGuid, eventSourceName, traits);
689 internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
692 // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
693 // On other architectures it is a no-op.
695 // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
696 // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
698 // 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.
700 eventSourceGuid = Guid.Empty;
701 eventSourceName = null;
703 manifestBytes = null;
709 /// This method is called when the eventSource is updated by the controller.
711 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
713 #pragma warning disable 1591
714 // optimized for common signatures (no args)
715 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
716 protected unsafe void WriteEvent(int eventId)
718 WriteEventCore(eventId, 0, null);
721 // optimized for common signatures (ints)
722 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
723 protected unsafe void WriteEvent(int eventId, int arg1)
725 if (m_eventSourceEnabled)
727 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
728 descrs[0].DataPointer = (IntPtr)(&arg1);
730 WriteEventCore(eventId, 1, descrs);
734 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
735 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
737 if (m_eventSourceEnabled)
739 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
740 descrs[0].DataPointer = (IntPtr)(&arg1);
742 descrs[1].DataPointer = (IntPtr)(&arg2);
744 WriteEventCore(eventId, 2, descrs);
748 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
749 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
751 if (m_eventSourceEnabled)
753 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
754 descrs[0].DataPointer = (IntPtr)(&arg1);
756 descrs[1].DataPointer = (IntPtr)(&arg2);
758 descrs[2].DataPointer = (IntPtr)(&arg3);
760 WriteEventCore(eventId, 3, descrs);
764 // optimized for common signatures (longs)
765 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
766 protected unsafe void WriteEvent(int eventId, long arg1)
768 if (m_eventSourceEnabled)
770 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
771 descrs[0].DataPointer = (IntPtr)(&arg1);
773 WriteEventCore(eventId, 1, descrs);
777 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
778 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
780 if (m_eventSourceEnabled)
782 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
783 descrs[0].DataPointer = (IntPtr)(&arg1);
785 descrs[1].DataPointer = (IntPtr)(&arg2);
787 WriteEventCore(eventId, 2, descrs);
791 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
792 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
794 if (m_eventSourceEnabled)
796 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
797 descrs[0].DataPointer = (IntPtr)(&arg1);
799 descrs[1].DataPointer = (IntPtr)(&arg2);
801 descrs[2].DataPointer = (IntPtr)(&arg3);
803 WriteEventCore(eventId, 3, descrs);
807 // optimized for common signatures (strings)
808 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
809 protected unsafe void WriteEvent(int eventId, string arg1)
811 if (m_eventSourceEnabled)
813 if (arg1 == null) arg1 = "";
814 fixed (char* string1Bytes = arg1)
816 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
817 descrs[0].DataPointer = (IntPtr)string1Bytes;
818 descrs[0].Size = ((arg1.Length + 1) * 2);
819 WriteEventCore(eventId, 1, descrs);
824 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
825 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
827 if (m_eventSourceEnabled)
829 if (arg1 == null) arg1 = "";
830 if (arg2 == null) arg2 = "";
831 fixed (char* string1Bytes = arg1)
832 fixed (char* string2Bytes = arg2)
834 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
835 descrs[0].DataPointer = (IntPtr)string1Bytes;
836 descrs[0].Size = ((arg1.Length + 1) * 2);
837 descrs[1].DataPointer = (IntPtr)string2Bytes;
838 descrs[1].Size = ((arg2.Length + 1) * 2);
839 WriteEventCore(eventId, 2, descrs);
844 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
845 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
847 if (m_eventSourceEnabled)
849 if (arg1 == null) arg1 = "";
850 if (arg2 == null) arg2 = "";
851 if (arg3 == null) arg3 = "";
852 fixed (char* string1Bytes = arg1)
853 fixed (char* string2Bytes = arg2)
854 fixed (char* string3Bytes = arg3)
856 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
857 descrs[0].DataPointer = (IntPtr)string1Bytes;
858 descrs[0].Size = ((arg1.Length + 1) * 2);
859 descrs[1].DataPointer = (IntPtr)string2Bytes;
860 descrs[1].Size = ((arg2.Length + 1) * 2);
861 descrs[2].DataPointer = (IntPtr)string3Bytes;
862 descrs[2].Size = ((arg3.Length + 1) * 2);
863 WriteEventCore(eventId, 3, descrs);
868 // optimized for common signatures (string and ints)
869 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
870 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
872 if (m_eventSourceEnabled)
874 if (arg1 == null) arg1 = "";
875 fixed (char* string1Bytes = arg1)
877 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
878 descrs[0].DataPointer = (IntPtr)string1Bytes;
879 descrs[0].Size = ((arg1.Length + 1) * 2);
880 descrs[1].DataPointer = (IntPtr)(&arg2);
882 WriteEventCore(eventId, 2, descrs);
887 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
888 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
890 if (m_eventSourceEnabled)
892 if (arg1 == null) arg1 = "";
893 fixed (char* string1Bytes = arg1)
895 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
896 descrs[0].DataPointer = (IntPtr)string1Bytes;
897 descrs[0].Size = ((arg1.Length + 1) * 2);
898 descrs[1].DataPointer = (IntPtr)(&arg2);
900 descrs[2].DataPointer = (IntPtr)(&arg3);
902 WriteEventCore(eventId, 3, descrs);
907 // optimized for common signatures (string and longs)
908 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
909 protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
911 if (m_eventSourceEnabled)
913 if (arg1 == null) arg1 = "";
914 fixed (char* string1Bytes = arg1)
916 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
917 descrs[0].DataPointer = (IntPtr)string1Bytes;
918 descrs[0].Size = ((arg1.Length + 1) * 2);
919 descrs[1].DataPointer = (IntPtr)(&arg2);
921 WriteEventCore(eventId, 2, descrs);
926 // optimized for common signatures (long and string)
927 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
928 protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
930 if (m_eventSourceEnabled)
932 if (arg2 == null) arg2 = "";
933 fixed (char* string2Bytes = arg2)
935 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
936 descrs[0].DataPointer = (IntPtr)(&arg1);
938 descrs[1].DataPointer = (IntPtr)string2Bytes;
939 descrs[1].Size = ((arg2.Length + 1) * 2);
940 WriteEventCore(eventId, 2, descrs);
945 // optimized for common signatures (int and string)
946 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
947 protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
949 if (m_eventSourceEnabled)
951 if (arg2 == null) arg2 = "";
952 fixed (char* string2Bytes = arg2)
954 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
955 descrs[0].DataPointer = (IntPtr)(&arg1);
957 descrs[1].DataPointer = (IntPtr)string2Bytes;
958 descrs[1].Size = ((arg2.Length + 1) * 2);
959 WriteEventCore(eventId, 2, descrs);
964 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
965 protected unsafe void WriteEvent(int eventId, byte[] arg1)
967 if (m_eventSourceEnabled)
969 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
970 if (arg1 == null || arg1.Length == 0)
973 descrs[0].DataPointer = (IntPtr)(&blobSize);
975 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
977 WriteEventCore(eventId, 2, descrs);
981 int blobSize = arg1.Length;
982 fixed (byte* blob = &arg1[0])
984 descrs[0].DataPointer = (IntPtr)(&blobSize);
986 descrs[1].DataPointer = (IntPtr)blob;
987 descrs[1].Size = blobSize;
988 WriteEventCore(eventId, 2, descrs);
994 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
995 protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
997 if (m_eventSourceEnabled)
999 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1000 descrs[0].DataPointer = (IntPtr)(&arg1);
1002 if (arg2 == null || arg2.Length == 0)
1005 descrs[1].DataPointer = (IntPtr)(&blobSize);
1007 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1009 WriteEventCore(eventId, 3, descrs);
1013 int blobSize = arg2.Length;
1014 fixed (byte* blob = &arg2[0])
1016 descrs[1].DataPointer = (IntPtr)(&blobSize);
1018 descrs[2].DataPointer = (IntPtr)blob;
1019 descrs[2].Size = blobSize;
1020 WriteEventCore(eventId, 3, descrs);
1026 #pragma warning restore 1591
1029 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1031 protected internal struct EventData
1034 /// Address where the one argument lives (if this points to managed memory you must ensure the
1035 /// managed object is pinned.
1037 public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
1039 /// Size of the argument referenced by DataPointer
1041 public int Size { get { return m_Size; } set { m_Size = value; } }
1045 /// Initializes the members of this EventData object to point at a previously-pinned
1046 /// tracelogging-compatible metadata blob.
1048 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1049 /// <param name="size">The size of the metadata blob.</param>
1050 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1051 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1053 this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
1055 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1058 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1059 // the way EventWrite wants it.
1060 internal long m_Ptr;
1061 internal int m_Size;
1062 #pragma warning disable 0649
1063 internal int m_Reserved; // Used to pad the size to match the Win32 API
1064 #pragma warning restore 0649
1069 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1070 /// do this, while straightforward, is unsafe.
1074 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1076 /// if (IsEnabled())
1078 /// if (arg2 == null) arg2 = "";
1079 /// fixed (char* string2Bytes = arg2)
1081 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1082 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1083 /// descrs[0].Size = 8;
1084 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1085 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1086 /// WriteEventCore(eventId, 2, descrs);
1092 [CLSCompliant(false)]
1093 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1095 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1099 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1100 /// that you use to do this, while straightforward, is unsafe. The only difference from
1101 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1105 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1107 /// if (IsEnabled())
1109 /// if (arg2 == null) arg2 = "";
1110 /// fixed (char* string2Bytes = arg2)
1112 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1113 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1114 /// descrs[0].Size = 8;
1115 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1116 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1117 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1123 [CLSCompliant(false)]
1124 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1126 if (m_eventSourceEnabled)
1130 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1131 if (relatedActivityId != null)
1132 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1134 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1135 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1136 Guid* pActivityId = null;
1137 Guid activityId = Guid.Empty;
1138 Guid relActivityId = Guid.Empty;
1140 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1141 ((activityOptions & EventActivityOptions.Disable) == 0))
1143 if (opcode == EventOpcode.Start)
1145 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1147 else if (opcode == EventOpcode.Stop)
1149 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1152 if (activityId != Guid.Empty)
1153 pActivityId = &activityId;
1154 if (relActivityId != Guid.Empty)
1155 relatedActivityId = &relActivityId;
1158 #if FEATURE_MANAGED_ETW
1159 if (m_eventData[eventId].EnabledForETW)
1162 #if FEATURE_ACTIVITYSAMPLING
1163 // this code should be kept in sync with WriteEventVarargs().
1164 SessionMask etwSessions = SessionMask.All;
1165 // only compute etwSessions if there are *any* ETW filters enabled...
1166 if ((ulong)m_curLiveSessions != 0)
1167 etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
1168 // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
1169 // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
1171 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1173 if (!SelfDescribingEvents)
1175 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1177 // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
1178 // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
1179 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1180 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1181 // synthesize a new one
1182 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1183 ThrowEventSourceException(m_eventData[eventId].Name);
1187 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1188 // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
1189 // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
1190 // only some of the ETW sessions will receive this event. Synthesize a new
1191 // Descriptor whose Keywords field will have the appropriate bits set.
1192 // etwSessions might be 0, if there are legacy ETW listeners that want this event
1193 var desc = new EventDescriptor(
1194 m_eventData[eventId].Descriptor.EventId,
1195 m_eventData[eventId].Descriptor.Version,
1196 m_eventData[eventId].Descriptor.Channel,
1197 m_eventData[eventId].Descriptor.Level,
1198 m_eventData[eventId].Descriptor.Opcode,
1199 m_eventData[eventId].Descriptor.Task,
1200 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1202 if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1203 ThrowEventSourceException(m_eventData[eventId].Name);
1208 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1211 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1213 m_eventData[eventId].Parameters);
1214 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1217 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1218 // TODO: activity ID support
1219 EventSourceOptions opt = new EventSourceOptions
1221 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1222 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1223 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1226 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1230 if (!SelfDescribingEvents)
1232 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1233 ThrowEventSourceException(m_eventData[eventId].Name);
1237 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1240 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1241 m_eventData[eventId].Tags,
1242 m_eventData[eventId].Parameters);
1243 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1246 EventSourceOptions opt = new EventSourceOptions
1248 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1249 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1250 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1253 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1255 #endif // FEATURE_ACTIVITYSAMPLING
1257 #endif // FEATURE_MANAGED_ETW
1259 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1260 WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
1262 catch (Exception ex)
1264 if (ex is EventSourceException)
1267 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1272 // fallback varags helpers.
1274 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1275 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1276 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1277 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1278 /// check so that the varargs call is not made when the EventSource is not active.
1280 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1281 protected unsafe void WriteEvent(int eventId, params object[] args)
1283 WriteEventVarargs(eventId, null, args);
1287 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1288 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1289 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1290 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1291 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1292 /// check so that the varargs call is not made when the EventSource is not active.
1294 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1296 WriteEventVarargs(eventId, &relatedActivityId, args);
1301 #region IDisposable Members
1303 /// Disposes of an EventSource.
1305 public void Dispose()
1308 GC.SuppressFinalize(this);
1311 /// Disposes of an EventSource.
1314 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1316 /// 1. We may be called more than once: do nothing after the first call.
1317 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1319 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1320 protected virtual void Dispose(bool disposing)
1324 #if FEATURE_MANAGED_ETW
1325 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1326 // even in scenarios with a high volume of ETW events.
1327 if (m_eventSourceEnabled)
1331 SendManifest(m_rawManifest);
1334 { } // If it fails, simply give up.
1335 m_eventSourceEnabled = false;
1337 if (m_provider != null)
1339 m_provider.Dispose();
1344 m_eventSourceEnabled = false;
1345 m_eventSourceDisposed = true;
1348 /// Finalizer for EventSource
1352 this.Dispose(false);
1357 #if FEATURE_ACTIVITYSAMPLING
1358 internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
1360 Debug.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
1362 if (m_eventSourceEnabled)
1364 if (listener == null)
1366 WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
1370 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1371 eventCallbackArgs.EventId = 0;
1372 eventCallbackArgs.Message = msg;
1373 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
1374 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
1375 eventCallbackArgs.EventName = "EventSourceMessage";
1376 listener.OnEventWritten(eventCallbackArgs);
1382 private unsafe void WriteEventRaw(
1384 ref EventDescriptor eventDescriptor,
1386 Guid* relatedActivityID,
1390 #if FEATURE_MANAGED_ETW
1391 if (m_provider == null)
1393 ThrowEventSourceException(eventName);
1397 if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
1398 ThrowEventSourceException(eventName);
1400 #endif // FEATURE_MANAGED_ETW
1403 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1404 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1405 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1406 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1409 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1410 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1412 m_config = ValidateSettings(settings);
1413 Initialize(eventSourceGuid, eventSourceName, traits);
1417 /// This method is responsible for the common initialization path from our constructors. It must
1418 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1419 /// "Log", such an exception would become a cached exception for the initialization of the static
1420 /// member, and any future access to the "Log" would throw the cached exception).
1422 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1423 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1428 if (m_traits != null && m_traits.Length % 2 != 0)
1430 throw new ArgumentException(Resources.GetResourceString("TraitEven"), nameof(traits));
1433 if (eventSourceGuid == Guid.Empty)
1435 throw new ArgumentException(Resources.GetResourceString("EventSource_NeedGuid"));
1438 if (eventSourceName == null)
1440 throw new ArgumentException(Resources.GetResourceString("EventSource_NeedName"));
1443 m_name = eventSourceName;
1444 m_guid = eventSourceGuid;
1445 #if FEATURE_ACTIVITYSAMPLING
1446 m_curLiveSessions = new SessionMask(0);
1447 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
1448 #endif // FEATURE_ACTIVITYSAMPLING
1450 //Enable Implicit Activity tracker
1451 m_activityTracker = ActivityTracker.Instance;
1453 #if FEATURE_MANAGED_ETW
1454 // Create and register our provider traits. We do this early because it is needed to log errors
1455 // In the self-describing event case.
1456 this.InitializeProviderMetadata();
1458 // Register the provider with ETW
1459 var provider = new OverideEventProvider(this);
1460 provider.Register(eventSourceGuid);
1462 // Add the eventSource to the global (weak) list.
1463 // This also sets m_id, which is the index in the list.
1464 EventListener.AddEventSource(this);
1466 #if FEATURE_MANAGED_ETW
1467 // OK if we get this far without an exception, then we can at least write out error messages.
1468 // Set m_provider, which allows this.
1469 m_provider = provider;
1471 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN && !PLATFORM_UNIX)
1472 // API available on OS >= Win 8 and patched Win 7.
1473 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1474 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1477 int setInformationResult;
1478 System.Runtime.InteropServices.GCHandle metadataHandle =
1479 System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1480 IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1482 setInformationResult = m_provider.SetInformation(
1483 UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1485 (uint)this.providerMetadata.Length);
1487 metadataHandle.Free();
1489 #endif // FEATURE_MANAGED_ETW
1491 Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1492 // We are logically completely initialized at this point.
1493 m_completelyInited = true;
1497 if (m_constructionException == null)
1498 m_constructionException = e;
1499 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1502 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1503 lock (EventListener.EventListenersLock)
1505 // If there are any deferred commands, we can do them now.
1506 // This is the most likely place for exceptions to happen.
1507 // Note that we are NOT resetting m_deferredCommands to NULL here,
1508 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1509 EventCommandEventArgs deferredCommands = m_deferredCommands;
1510 while (deferredCommands != null)
1512 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1513 deferredCommands = deferredCommands.nextCommand;
1518 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1520 if (eventSourceType == null)
1521 throw new ArgumentNullException(nameof(eventSourceType));
1522 Contract.EndContractBlock();
1524 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1525 if (attrib != null && attrib.Name != null)
1528 return eventSourceType.Name;
1532 /// Implements the SHA1 hashing algorithm. Note that this
1533 /// implementation is for hashing public information. Do not
1534 /// use this code to hash private data, as this implementation does
1535 /// not take any steps to avoid information disclosure.
1537 private struct Sha1ForNonSecretPurposes
1539 private long length; // Total message length in bits
1540 private uint[] w; // Workspace
1541 private int pos; // Length of current chunk in bytes
1544 /// Call Start() to initialize the hash object.
1550 this.w = new uint[85];
1555 this.w[80] = 0x67452301;
1556 this.w[81] = 0xEFCDAB89;
1557 this.w[82] = 0x98BADCFE;
1558 this.w[83] = 0x10325476;
1559 this.w[84] = 0xC3D2E1F0;
1563 /// Adds an input byte to the hash.
1565 /// <param name="input">Data to include in the hash.</param>
1566 public void Append(byte input)
1568 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1569 if (64 == ++this.pos)
1576 /// Adds input bytes to the hash.
1578 /// <param name="input">
1579 /// Data to include in the hash. Must not be null.
1581 public void Append(byte[] input)
1583 foreach (var b in input)
1590 /// Retrieves the hash value.
1591 /// Note that after calling this function, the hash object should
1592 /// be considered uninitialized. Subsequent calls to Append or
1593 /// Finish will produce useless results. Call Start() to
1596 /// <param name="output">
1597 /// Buffer to receive the hash value. Must not be null.
1598 /// Up to 20 bytes of hash will be written to the output buffer.
1599 /// If the buffer is smaller than 20 bytes, the remaining hash
1600 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1601 /// rest of the buffer is left unmodified.
1603 public void Finish(byte[] output)
1605 long l = this.length + 8 * this.pos;
1607 while (this.pos != 56)
1614 this.Append((byte)(l >> 56));
1615 this.Append((byte)(l >> 48));
1616 this.Append((byte)(l >> 40));
1617 this.Append((byte)(l >> 32));
1618 this.Append((byte)(l >> 24));
1619 this.Append((byte)(l >> 16));
1620 this.Append((byte)(l >> 8));
1621 this.Append((byte)l);
1623 int end = output.Length < 20 ? output.Length : 20;
1624 for (int i = 0; i != end; i++)
1626 uint temp = this.w[80 + i / 4];
1627 output[i] = (byte)(temp >> 24);
1628 this.w[80 + i / 4] = temp << 8;
1634 /// Called when this.pos reaches 64.
1636 private void Drain()
1638 for (int i = 16; i != 80; i++)
1640 this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1645 uint a = this.w[80];
1646 uint b = this.w[81];
1647 uint c = this.w[82];
1648 uint d = this.w[83];
1649 uint e = this.w[84];
1651 for (int i = 0; i != 20; i++)
1653 const uint k = 0x5A827999;
1654 uint f = (b & c) | ((~b) & d);
1655 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1658 for (int i = 20; i != 40; i++)
1661 const uint k = 0x6ED9EBA1;
1662 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1665 for (int i = 40; i != 60; i++)
1667 uint f = (b & c) | (b & d) | (c & d);
1668 const uint k = 0x8F1BBCDC;
1669 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1672 for (int i = 60; i != 80; i++)
1675 const uint k = 0xCA62C1D6;
1676 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1686 this.length += 512; // 64 bytes == 512 bits
1690 private static uint Rol1(uint input)
1692 return (input << 1) | (input >> 31);
1695 private static uint Rol5(uint input)
1697 return (input << 5) | (input >> 27);
1700 private static uint Rol30(uint input)
1702 return (input << 30) | (input >> 2);
1706 private static Guid GenerateGuidFromName(string name)
1708 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1709 var hash = new Sha1ForNonSecretPurposes();
1711 hash.Append(namespaceBytes);
1713 Array.Resize(ref bytes, 16);
1716 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1717 return new Guid(bytes);
1720 private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1722 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1723 // the recursion, but can we do this in a robust way?
1725 IntPtr dataPointer = data->DataPointer;
1726 // advance to next EventData in array
1729 Type dataType = GetDataType(m_eventData[eventId], parameterId);
1732 if (dataType == typeof(IntPtr))
1734 return *((IntPtr*)dataPointer);
1736 else if (dataType == typeof(int))
1738 return *((int*)dataPointer);
1740 else if (dataType == typeof(uint))
1742 return *((uint*)dataPointer);
1744 else if (dataType == typeof(long))
1746 return *((long*)dataPointer);
1748 else if (dataType == typeof(ulong))
1750 return *((ulong*)dataPointer);
1752 else if (dataType == typeof(byte))
1754 return *((byte*)dataPointer);
1756 else if (dataType == typeof(sbyte))
1758 return *((sbyte*)dataPointer);
1760 else if (dataType == typeof(short))
1762 return *((short*)dataPointer);
1764 else if (dataType == typeof(ushort))
1766 return *((ushort*)dataPointer);
1768 else if (dataType == typeof(float))
1770 return *((float*)dataPointer);
1772 else if (dataType == typeof(double))
1774 return *((double*)dataPointer);
1776 else if (dataType == typeof(decimal))
1778 return *((decimal*)dataPointer);
1780 else if (dataType == typeof(bool))
1782 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1783 if (*((int*)dataPointer) == 1)
1792 else if (dataType == typeof(Guid))
1794 return *((Guid*)dataPointer);
1796 else if (dataType == typeof(char))
1798 return *((char*)dataPointer);
1800 else if (dataType == typeof(DateTime))
1802 long dateTimeTicks = *((long*)dataPointer);
1803 return DateTime.FromFileTimeUtc(dateTimeTicks);
1805 else if (dataType == typeof(byte[]))
1807 // byte[] are written to EventData* as an int followed by a blob
1808 int cbSize = *((int*)dataPointer);
1809 byte[] blob = new byte[cbSize];
1810 dataPointer = data->DataPointer;
1812 for (int i = 0; i < cbSize; ++i)
1813 blob[i] = *((byte*)(dataPointer + i));
1816 else if (dataType == typeof(byte*))
1818 // TODO: how do we want to handle this? For now we ignore it...
1823 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1830 m_EventSourceInDecodeObject = true;
1832 if (dataType.IsEnum())
1834 dataType = Enum.GetUnderlyingType(dataType);
1839 // Everything else is marshaled as a string.
1840 // ETW strings are NULL-terminated, so marshal everything up to the first
1841 // null in the string.
1842 //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1843 if(dataPointer == IntPtr.Zero)
1848 return new string((char *)dataPointer);
1853 m_EventSourceInDecodeObject = false;
1858 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1860 private EventDispatcher GetDispatcher(EventListener listener)
1862 EventDispatcher dispatcher = m_Dispatchers;
1863 while (dispatcher != null)
1865 if (dispatcher.m_Listener == listener)
1867 dispatcher = dispatcher.m_Next;
1872 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1874 if (m_eventSourceEnabled)
1878 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1879 if (childActivityID != null)
1881 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1883 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1884 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1885 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1886 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1887 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1888 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1889 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1890 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1891 if (!m_eventData[eventId].HasRelatedActivityID)
1893 throw new ArgumentException(Resources.GetResourceString("EventSource_NoRelatedActivityId"));
1897 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1899 Guid* pActivityId = null;
1900 Guid activityId = Guid.Empty;
1901 Guid relatedActivityId = Guid.Empty;
1902 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1903 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1905 if (childActivityID == null &&
1906 ((activityOptions & EventActivityOptions.Disable) == 0))
1908 if (opcode == EventOpcode.Start)
1910 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1912 else if (opcode == EventOpcode.Stop)
1914 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1917 if (activityId != Guid.Empty)
1918 pActivityId = &activityId;
1919 if (relatedActivityId != Guid.Empty)
1920 childActivityID = &relatedActivityId;
1923 #if FEATURE_MANAGED_ETW
1924 if (m_eventData[eventId].EnabledForETW)
1926 #if FEATURE_ACTIVITYSAMPLING
1927 // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
1928 SessionMask etwSessions = SessionMask.All;
1929 // only compute etwSessions if there are *any* ETW filters enabled...
1930 if ((ulong)m_curLiveSessions != 0)
1931 etwSessions = GetEtwSessionMask(eventId, childActivityID);
1933 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1935 if (!SelfDescribingEvents)
1937 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1939 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1940 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1941 // synthesize a new one
1942 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1943 ThrowEventSourceException(m_eventData[eventId].Name);
1947 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1948 // only some of the ETW sessions will receive this event. Synthesize a new
1949 // Descriptor whose Keywords field will have the appropriate bits set.
1950 var desc = new EventDescriptor(
1951 m_eventData[eventId].Descriptor.EventId,
1952 m_eventData[eventId].Descriptor.Version,
1953 m_eventData[eventId].Descriptor.Channel,
1954 m_eventData[eventId].Descriptor.Level,
1955 m_eventData[eventId].Descriptor.Opcode,
1956 m_eventData[eventId].Descriptor.Task,
1957 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1959 if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
1960 ThrowEventSourceException(m_eventData[eventId].Name);
1965 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1968 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1970 m_eventData[eventId].Parameters);
1971 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1974 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1975 // TODO: activity ID support
1976 EventSourceOptions opt = new EventSourceOptions
1978 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1979 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1980 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1983 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1987 if (!SelfDescribingEvents)
1989 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1990 ThrowEventSourceException(m_eventData[eventId].Name);
1994 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1997 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1999 m_eventData[eventId].Parameters);
2000 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2003 // TODO: activity ID support
2004 EventSourceOptions opt = new EventSourceOptions
2006 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
2007 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2008 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2011 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2013 #endif // FEATURE_ACTIVITYSAMPLING
2015 #endif // FEATURE_MANAGED_ETW
2016 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
2018 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
2019 // Maintain old behavior - object identity is preserved
2020 if (AppContextSwitches.PreserveEventListnerObjectIdentity)
2022 WriteToAllListeners(eventId, childActivityID, args);
2025 #endif // !ES_BUILD_STANDALONE
2027 object[] serializedArgs = SerializeEventArgs(eventId, args);
2028 WriteToAllListeners(eventId, childActivityID, serializedArgs);
2032 catch (Exception ex)
2034 if (ex is EventSourceException)
2037 ThrowEventSourceException(m_eventData[eventId].Name, ex);
2042 unsafe private object[] SerializeEventArgs(int eventId, object[] args)
2044 TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2045 if (eventTypes == null)
2047 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2049 m_eventData[eventId].Parameters);
2050 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2052 var eventData = new object[eventTypes.typeInfos.Length];
2053 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2055 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2061 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2062 /// checks that they in fact match and logs a warning to the debugger if they don't.
2064 /// <param name="infos"></param>
2065 /// <param name="args"></param>
2066 private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
2068 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2069 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2070 // writing to the debugger log on PCL.
2071 bool typesMatch = args.Length == infos.Length;
2074 while (typesMatch && i < args.Length)
2076 Type pType = infos[i].ParameterType;
2078 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2079 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
2080 // argument is null and the parameter type is non-nullable.
2081 if ((args[i] != null && (args[i].GetType() != pType))
2082 || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
2094 System.Diagnostics.Debugger.Log(0, null, Resources.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
2096 #endif //!ES_BUILD_PCL
2099 private int GetParamLenghtIncludingByteArray(ParameterInfo[] parameters)
2102 foreach (ParameterInfo info in parameters)
2104 if (info.ParameterType == typeof(byte[]))
2117 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2119 // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
2120 // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
2121 // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
2122 // we just used the modifiedParamCount here -- so we need both.
2123 int paramCount = m_eventData[eventId].Parameters.Length;
2124 int modifiedParamCount = GetParamLenghtIncludingByteArray(m_eventData[eventId].Parameters);
2125 if (eventDataCount != modifiedParamCount)
2127 ReportOutOfBandMessage(Resources.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
2128 paramCount = Math.Min(paramCount, eventDataCount);
2131 object[] args = new object[paramCount];
2133 EventSource.EventData* dataPtr = data;
2134 for (int i = 0; i < paramCount; i++)
2135 args[i] = DecodeObject(eventId, i, ref dataPtr);
2136 WriteToAllListeners(eventId, childActivityID, args);
2139 // helper for writing to all EventListeners attached the current eventSource.
2140 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
2142 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2143 eventCallbackArgs.EventId = eventId;
2144 if (childActivityID != null)
2145 eventCallbackArgs.RelatedActivityId = *childActivityID;
2146 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2147 eventCallbackArgs.Message = m_eventData[eventId].Message;
2148 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2150 DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2153 private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2155 Exception lastThrownException = null;
2156 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2158 Debug.Assert(dispatcher.m_EventEnabled != null);
2159 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2161 #if FEATURE_ACTIVITYSAMPLING
2162 var activityFilter = dispatcher.m_Listener.m_activityFilter;
2163 // order below is important as PassesActivityFilter will "flow" active activities
2164 // even when the current EventSource doesn't have filtering enabled. This allows
2165 // interesting activities to be updated so that sources that do sample can get
2167 if (activityFilter == null ||
2168 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2169 m_eventData[eventId].TriggersActivityTracking > 0,
2171 !dispatcher.m_activityFilteringEnabled)
2172 #endif // FEATURE_ACTIVITYSAMPLING
2176 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2180 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2181 + e.Message, false);
2182 lastThrownException = e;
2188 if (lastThrownException != null)
2190 throw new EventSourceException(lastThrownException);
2194 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2195 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2197 #if FEATURE_MANAGED_ETW
2198 if (m_provider != null)
2200 string eventName = "EventSourceMessage";
2201 if (SelfDescribingEvents)
2203 EventSourceOptions opt = new EventSourceOptions
2205 Keywords = (EventKeywords)unchecked(keywords),
2208 var msg = new { message = msgString };
2209 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2210 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2214 // We want the name of the provider to show up so if we don't have a manifest we create
2215 // on that at least has the provider name (I don't define any events).
2216 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2218 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2219 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2220 manifestBuilder.AddEventParameter(typeof(string), "message");
2221 manifestBuilder.EndEvent();
2222 SendManifest(manifestBuilder.CreateManifest());
2225 // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2226 fixed (char* msgStringPtr = msgString)
2228 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2229 EventProvider.EventData data = new EventProvider.EventData();
2230 data.Ptr = (ulong)msgStringPtr;
2231 data.Size = (uint)(2 * (msgString.Length + 1));
2233 m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
2237 #endif // FEATURE_MANAGED_ETW
2241 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2242 /// while writing the message to any one of the listeners will be silently ignored.
2244 private void WriteStringToAllListeners(string eventName, string msg)
2246 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2247 eventCallbackArgs.EventId = 0;
2248 eventCallbackArgs.Message = msg;
2249 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2250 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2251 eventCallbackArgs.EventName = eventName;
2253 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2255 bool dispatcherEnabled = false;
2256 if (dispatcher.m_EventEnabled == null)
2258 // if the listeners that weren't correctly initialized, we will send to it
2259 // since this is an error message and we want to see it go out.
2260 dispatcherEnabled = true;
2264 // if there's *any* enabled event on the dispatcher we'll write out the string
2265 // otherwise we'll treat the listener as disabled and skip it
2266 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2268 if (dispatcher.m_EventEnabled[evtId])
2270 dispatcherEnabled = true;
2277 if (dispatcherEnabled)
2278 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2282 // ignore any exceptions thrown by listeners' OnEventWritten
2287 #if FEATURE_ACTIVITYSAMPLING
2288 unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
2290 SessionMask etwSessions = new SessionMask();
2292 for (int i = 0; i < SessionMask.MAX; ++i)
2294 EtwSession etwSession = m_etwSessionIdMap[i];
2295 if (etwSession != null)
2297 ActivityFilter activityFilter = etwSession.m_activityFilter;
2298 // PassesActivityFilter() will flow "interesting" activities, so make sure
2299 // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
2300 // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
2301 // do not fire events indiscriminately, when no filters are specified, but only
2302 // if, in addition, the session did not also enable ActivitySampling)
2303 if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
2304 activityFilter != null &&
2305 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2306 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
2307 !m_activityFilteringForETWEnabled[i])
2309 etwSessions[i] = true;
2313 // flow "interesting" activities for all legacy sessions in which there's some
2314 // level of activity tracing enabled (even other EventSources)
2315 if (m_legacySessions != null && m_legacySessions.Count > 0 &&
2316 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
2318 // only calculate InternalCurrentThreadActivityId once
2319 Guid* pCurrentActivityId = null;
2320 Guid currentActivityId;
2321 foreach (var legacyEtwSession in m_legacySessions)
2323 if (legacyEtwSession == null)
2326 ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
2327 if (activityFilter != null)
2329 if (pCurrentActivityId == null)
2331 currentActivityId = InternalCurrentThreadActivityId;
2332 pCurrentActivityId = ¤tActivityId;
2334 ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
2341 #endif // FEATURE_ACTIVITYSAMPLING
2344 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2345 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2347 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2352 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2353 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2355 #if FEATURE_MANAGED_ETW_CHANNELS
2356 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2358 EventChannel channel = EventChannel.None;
2361 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2364 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2365 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2370 // does is pass the level test?
2371 if ((currentLevel != 0) && (currentLevel < eventLevel))
2374 // if yes, does it pass the keywords test?
2375 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2377 #if FEATURE_MANAGED_ETW_CHANNELS
2378 // is there a channel with keywords that match currentMatchAnyKeyword?
2379 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2381 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2382 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2388 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2395 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2396 private void ThrowEventSourceException(string eventName, Exception innerEx = null)
2398 // If we fail during out of band logging we may end up trying
2399 // to throw another EventSourceException, thus hitting a StackOverflowException.
2400 // Avoid StackOverflow by making sure we do not recursively call this method.
2401 if (m_EventSourceExceptionRecurenceCount > 0)
2405 m_EventSourceExceptionRecurenceCount++;
2407 string errorPrefix = "EventSourceException";
2408 if (eventName != null)
2410 errorPrefix += " while processing event \"" + eventName + "\"";
2413 // TODO Create variations of EventSourceException that indicate more information using the error code.
2414 switch (EventProvider.GetLastWriteEventError())
2416 case EventProvider.WriteEventErrorCode.EventTooBig:
2417 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_EventTooBig"), true);
2418 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_EventTooBig"), innerEx);
2420 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2421 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NoFreeBuffers"), true);
2422 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
2424 case EventProvider.WriteEventErrorCode.NullInput:
2425 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NullInput"), true);
2426 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NullInput"), innerEx);
2428 case EventProvider.WriteEventErrorCode.TooManyArgs:
2429 ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_TooManyArgs"), true);
2430 if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_TooManyArgs"), innerEx);
2433 if (innerEx != null)
2434 ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2436 ReportOutOfBandMessage(errorPrefix, true);
2437 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2443 m_EventSourceExceptionRecurenceCount--;
2447 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
2449 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2450 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2451 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2453 ThrowEventSourceException(eventName);
2457 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2459 if (opcode == EventOpcode.Info && eventName != null)
2461 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2463 return EventOpcode.Start;
2465 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2467 return EventOpcode.Stop;
2474 #if FEATURE_MANAGED_ETW
2476 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2478 private class OverideEventProvider : EventProvider
2480 public OverideEventProvider(EventSource eventSource)
2482 this.m_eventSource = eventSource;
2484 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2485 int perEventSourceSessionId, int etwSessionId)
2487 // We use null to represent the ETW EventListener.
2488 EventListener listener = null;
2489 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
2490 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2492 private EventSource m_eventSource;
2497 /// Used to hold all the static information about an event. This includes everything in the event
2498 /// descriptor as well as some stuff we added specifically for EventSource. see the
2499 /// code:m_eventData for where we use this.
2503 EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2504 now the move to CoreLib marked them as private.
2505 While they are technically private (it's a contract used between the library and the ILC toolchain),
2506 we need them to be rooted and exported from shared library for the system to work.
2507 For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2508 root them and modify shared library definition to force export them.
2515 partial struct EventMetadata
2517 public EventDescriptor Descriptor;
2518 public EventTags Tags;
2519 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2520 public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
2522 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2523 #if !FEATURE_ACTIVITYSAMPLING
2524 #pragma warning disable 0649
2526 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2527 #if !FEATURE_ACTIVITYSAMPLING
2528 #pragma warning restore 0649
2530 public string Name; // the name of the event
2531 public string Message; // If the event has a message associated with it, this is it.
2532 public ParameterInfo[] Parameters; // TODO can we remove?
2534 public TraceLoggingEventTypes TraceLoggingEventTypes;
2535 public EventActivityOptions ActivityOptions;
2538 public EventParameterType[] ParameterTypes;
2542 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2543 // eventSource. The logic is as follows
2545 // * if Command == Update
2546 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2547 // to (if listener != null)
2548 // perEventSourceSessionId = 0 - reserved for EventListeners
2549 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2550 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2551 // Keywords that identifies the session
2552 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2553 // discriminated by etwSessionId
2554 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2555 // activity tracing across different providers (which might have different sessionIds
2556 // for the same ETW session)
2557 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2558 // eventSource. In particular, if 'enabled' is false, 'level' and
2559 // 'matchAnyKeywords' are not used.
2560 // * OnEventCommand is invoked, which may cause calls to
2561 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2562 // depending on the logic in that routine.
2563 // * else (command != Update)
2564 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2565 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2567 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2568 internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
2569 EventCommand command, bool enable,
2570 EventLevel level, EventKeywords matchAnyKeyword,
2571 IDictionary<string, string> commandArguments)
2573 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2574 lock (EventListener.EventListenersLock)
2576 if (m_completelyInited)
2578 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2579 this.m_deferredCommands = null;
2580 // We are fully initialized, do the command
2581 DoCommand(commandArgs);
2585 // We can't do the command, simply remember it and we do it when we are fully constructed.
2586 commandArgs.nextCommand = m_deferredCommands;
2587 m_deferredCommands = commandArgs;
2593 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2594 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2595 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2596 /// This helper actually does all actual command logic.
2598 internal void DoCommand(EventCommandEventArgs commandArgs)
2600 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2601 // We defer commands until we are completely inited. This allows error messages to be sent.
2602 Debug.Assert(m_completelyInited);
2604 #if FEATURE_MANAGED_ETW
2605 if (m_provider == null) // If we failed to construct
2607 #endif // FEATURE_MANAGED_ETW
2609 m_outOfBandMessageCount = 0;
2610 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2613 EnsureDescriptorsInitialized();
2614 Debug.Assert(m_eventData != null);
2616 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2617 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2618 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2620 throw new ArgumentException(Resources.GetResourceString("EventSource_ListenerNotFound"));
2623 if (commandArgs.Arguments == null)
2624 commandArgs.Arguments = new Dictionary<string, string>();
2626 if (commandArgs.Command == EventCommand.Update)
2628 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2629 for (int i = 0; i < m_eventData.Length; i++)
2630 EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2632 if (commandArgs.enable)
2634 if (!m_eventSourceEnabled)
2636 // EventSource turned on for the first time, simply copy the bits.
2637 m_level = commandArgs.level;
2638 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2642 // Already enabled, make it the most verbose of the existing and new filter
2643 if (commandArgs.level > m_level)
2644 m_level = commandArgs.level;
2645 if (commandArgs.matchAnyKeyword == 0)
2646 m_matchAnyKeyword = 0;
2647 else if (m_matchAnyKeyword != 0)
2648 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2652 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2653 // represent 0-based positive values
2654 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2655 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2656 bSessionEnable = false;
2658 if (commandArgs.listener == null)
2660 if (!bSessionEnable)
2661 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2662 // for "global" enable/disable (passed in with listener == null and
2663 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2664 --commandArgs.perEventSourceSessionId;
2667 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2669 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2671 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2672 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2673 Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2675 // Send the manifest if we are enabling an ETW session
2676 if (bSessionEnable && commandArgs.dispatcher == null)
2678 // eventSourceDispatcher == null means this is the ETW manifest
2680 // Note that we unconditionally send the manifest whenever we are enabled, even if
2681 // we were already enabled. This is because there may be multiple sessions active
2682 // and we can't know that all the sessions have seen the manifest.
2683 if (!SelfDescribingEvents)
2684 SendManifest(m_rawManifest);
2687 #if FEATURE_ACTIVITYSAMPLING
2688 if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2690 bool participateInSampling = false;
2691 string activityFilters;
2694 ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
2695 out activityFilters, out sessionIdBit);
2697 if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
2699 throw new ArgumentException(Resources.GetResourceString("EventSource_SessionIdError",
2700 commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
2701 sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
2704 if (commandArgs.listener == null)
2706 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
2710 ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
2711 commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
2714 else if (!bSessionEnable && commandArgs.listener == null)
2716 // if we disable an ETW session, indicate that in a synthesized command argument
2717 if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
2719 commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
2722 #endif // FEATURE_ACTIVITYSAMPLING
2724 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2725 // things like log messages, or test if keywords are enabled in the callback.
2726 if (commandArgs.enable)
2728 Debug.Assert(m_eventData != null);
2729 m_eventSourceEnabled = true;
2732 this.OnEventCommand(commandArgs);
2733 var eventCommandCallback = this.m_eventCommandExecuted;
2734 if (eventCommandCallback != null)
2735 eventCommandCallback(this, commandArgs);
2737 #if FEATURE_ACTIVITYSAMPLING
2738 if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2740 // if we disable an ETW session, complete disabling it
2741 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
2743 #endif // FEATURE_ACTIVITYSAMPLING
2745 if (!commandArgs.enable)
2747 // If we are disabling, maybe we can turn on 'quick checks' to filter
2748 // quickly. These are all just optimizations (since later checks will still filter)
2750 #if FEATURE_ACTIVITYSAMPLING
2751 // Turn off (and forget) any information about Activity Tracing.
2752 if (commandArgs.listener == null)
2754 // reset all filtering information for activity-tracing-aware sessions
2755 for (int i = 0; i < SessionMask.MAX; ++i)
2757 EtwSession etwSession = m_etwSessionIdMap[i];
2758 if (etwSession != null)
2759 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2761 m_activityFilteringForETWEnabled = new SessionMask(0);
2762 m_curLiveSessions = new SessionMask(0);
2763 // reset activity-tracing-aware sessions
2764 if (m_etwSessionIdMap != null)
2765 for (int i = 0; i < SessionMask.MAX; ++i)
2766 m_etwSessionIdMap[i] = null;
2767 // reset legacy sessions
2768 if (m_legacySessions != null)
2769 m_legacySessions.Clear();
2773 ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
2774 commandArgs.dispatcher.m_activityFilteringEnabled = false;
2776 #endif // FEATURE_ACTIVITYSAMPLING
2778 // There is a good chance EnabledForAnyListener are not as accurate as
2779 // they could be, go ahead and get a better estimate.
2780 for (int i = 0; i < m_eventData.Length; i++)
2782 bool isEnabledForAnyListener = false;
2783 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2785 if (dispatcher.m_EventEnabled[i])
2787 isEnabledForAnyListener = true;
2791 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2794 // If no events are enabled, disable the global enabled bit.
2795 if (!AnyEventEnabled())
2798 m_matchAnyKeyword = 0;
2799 m_eventSourceEnabled = false;
2802 #if FEATURE_ACTIVITYSAMPLING
2803 UpdateKwdTriggers(commandArgs.enable);
2804 #endif // FEATURE_ACTIVITYSAMPLING
2808 if (commandArgs.Command == EventCommand.SendManifest)
2810 // TODO: should we generate the manifest here if we hadn't already?
2811 if (m_rawManifest != null)
2812 SendManifest(m_rawManifest);
2815 // These are not used for non-update commands and thus should always be 'default' values
2816 // Debug.Assert(enable == true);
2817 // Debug.Assert(level == EventLevel.LogAlways);
2818 // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2820 this.OnEventCommand(commandArgs);
2821 var eventCommandCallback = m_eventCommandExecuted;
2822 if (eventCommandCallback != null)
2823 eventCommandCallback(this, commandArgs);
2826 #if FEATURE_ACTIVITYSAMPLING
2827 if (m_completelyInited && (commandArgs.listener != null || shouldReport))
2829 SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
2830 ReportActivitySamplingInfo(commandArgs.listener, m);
2832 #endif // FEATURE_ACTIVITYSAMPLING
2836 // When the ETW session is created after the EventSource has registered with the ETW system
2837 // we can send any error messages here.
2838 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2839 // We never throw when doing a command.
2843 #if FEATURE_ACTIVITYSAMPLING
2845 internal void UpdateEtwSession(
2849 string activityFilters,
2850 bool participateInSampling)
2852 if (sessionIdBit < SessionMask.MAX)
2854 // activity-tracing-aware etw session
2857 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2858 ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
2859 m_etwSessionIdMap[sessionIdBit] = etwSession;
2860 m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
2864 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2865 m_etwSessionIdMap[sessionIdBit] = null;
2866 m_activityFilteringForETWEnabled[sessionIdBit] = false;
2867 if (etwSession != null)
2869 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2870 // the ETW session is going away; remove it from the global list
2871 EtwSession.RemoveEtwSession(etwSession);
2874 m_curLiveSessions[sessionIdBit] = bEnable;
2878 // legacy etw session
2881 if (m_legacySessions == null)
2882 m_legacySessions = new List<EtwSession>(8);
2883 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2884 if (!m_legacySessions.Contains(etwSession))
2885 m_legacySessions.Add(etwSession);
2889 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2890 if (etwSession != null)
2892 if (m_legacySessions != null)
2893 m_legacySessions.Remove(etwSession);
2894 // the ETW session is going away; remove it from the global list
2895 EtwSession.RemoveEtwSession(etwSession);
2901 internal static bool ParseCommandArgs(
2902 IDictionary<string, string> commandArguments,
2903 out bool participateInSampling,
2904 out string activityFilters,
2905 out int sessionIdBit)
2908 participateInSampling = false;
2909 string activityFilterString;
2910 if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
2912 // if a start event is specified default the event source to participate in sampling
2913 participateInSampling = true;
2916 if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
2918 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
2919 activityFilterString == "0")
2920 participateInSampling = false;
2922 participateInSampling = true;
2926 int sessionKwd = -1;
2927 if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
2928 !int.TryParse(sSessionKwd, out sessionKwd) ||
2929 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
2930 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
2937 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
2942 internal void UpdateKwdTriggers(bool enable)
2946 // recompute m_keywordTriggers
2947 ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
2949 gKeywords = 0xFFFFffffFFFFffff;
2951 m_keywordTriggers = 0;
2952 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
2954 EtwSession etwSession = m_etwSessionIdMap[sessId];
2955 if (etwSession == null)
2958 ActivityFilter activityFilter = etwSession.m_activityFilter;
2959 ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
2964 m_keywordTriggers = 0;
2968 #endif // FEATURE_ACTIVITYSAMPLING
2971 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2972 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2973 /// range return false, otherwise true.
2975 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
2977 if (dispatcher == null)
2979 if (eventId >= m_eventData.Length)
2981 #if FEATURE_MANAGED_ETW
2982 if (m_provider != null)
2983 m_eventData[eventId].EnabledForETW = value;
2988 if (eventId >= dispatcher.m_EventEnabled.Length)
2990 dispatcher.m_EventEnabled[eventId] = value;
2992 m_eventData[eventId].EnabledForAnyListener = true;
2998 /// Returns true if any event at all is on.
3000 private bool AnyEventEnabled()
3002 for (int i = 0; i < m_eventData.Length; i++)
3003 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
3008 private bool IsDisposed
3010 get { return m_eventSourceDisposed; }
3013 private void EnsureDescriptorsInitialized()
3015 #if !ES_BUILD_STANDALONE
3016 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3018 if (m_eventData == null)
3020 Guid eventSourceGuid = Guid.Empty;
3021 string eventSourceName = null;
3022 EventMetadata[] eventData = null;
3023 byte[] manifest = null;
3025 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
3026 // to the reflection approach.
3027 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
3029 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
3031 // GetMetadata failed, so we have to set it via reflection.
3032 Debug.Assert(m_rawManifest == null);
3033 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
3034 Debug.Assert(m_eventData != null);
3039 // GetMetadata worked, so set the fields as appropriate.
3040 m_name = eventSourceName;
3041 m_guid = eventSourceGuid;
3042 m_eventData = eventData;
3043 m_rawManifest = manifest;
3045 // TODO Enforce singleton pattern
3046 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
3048 EventSource eventSource = eventSourceRef.Target as EventSource;
3049 if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
3051 if (eventSource != this)
3053 throw new ArgumentException(Resources.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
3058 // Make certain all dispatchers also have their arrays initialized
3059 EventDispatcher dispatcher = m_Dispatchers;
3060 while (dispatcher != null)
3062 if (dispatcher.m_EventEnabled == null)
3063 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
3064 dispatcher = dispatcher.m_Next;
3067 if (s_currentPid == 0)
3069 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL && !CORECLR
3070 // for non-BCL EventSource we must assert SecurityPermission
3071 new SecurityPermission(PermissionState.Unrestricted).Assert();
3073 s_currentPid = Win32Native.GetCurrentProcessId();
3077 // Send out the ETW manifest XML out to ETW
3078 // Today, we only send the manifest to ETW, custom listeners don't get it.
3079 private unsafe bool SendManifest(byte[] rawManifest)
3081 bool success = true;
3083 if (rawManifest == null)
3086 Debug.Assert(!SelfDescribingEvents);
3088 #if FEATURE_MANAGED_ETW
3089 fixed (byte* dataPtr = rawManifest)
3091 // we don't want the manifest to show up in the event log channels so we specify as keywords
3092 // everything but the first 8 bits (reserved for the 8 channels)
3093 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
3094 ManifestEnvelope envelope = new ManifestEnvelope();
3096 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
3097 envelope.MajorVersion = 1;
3098 envelope.MinorVersion = 0;
3099 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
3100 int dataLeft = rawManifest.Length;
3101 envelope.ChunkNumber = 0;
3103 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
3105 dataDescrs[0].Ptr = (ulong)&envelope;
3106 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
3107 dataDescrs[0].Reserved = 0;
3109 dataDescrs[1].Ptr = (ulong)dataPtr;
3110 dataDescrs[1].Reserved = 0;
3112 int chunkSize = ManifestEnvelope.MaxChunkSize;
3113 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3114 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3115 while (dataLeft > 0)
3117 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3118 if (m_provider != null)
3120 if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
3122 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3123 // can fail. If we get this failure on the first chunk try again with something smaller
3124 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3125 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3127 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3129 chunkSize = chunkSize / 2;
3130 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3134 if (ThrowOnEventWriteErrors)
3135 ThrowEventSourceException("SendManifest");
3139 dataLeft -= chunkSize;
3140 dataDescrs[1].Ptr += (uint)chunkSize;
3141 envelope.ChunkNumber++;
3143 // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
3144 // 5 chunks, so only the largest manifests will hit the pause.
3145 if ((envelope.ChunkNumber % 5) == 0)
3147 RuntimeThread.Sleep(15);
3151 #endif // FEATURE_MANAGED_ETW
3156 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3158 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3162 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3163 // When that is the case, we have the build the custom assemblies on a member by hand.
3164 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3166 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3168 // Let the runtime to the work for us, since we can execute code in this context.
3169 Attribute firstAttribute = null;
3170 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3172 firstAttribute = (Attribute)attribute;
3175 return firstAttribute;
3178 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3179 // In the reflection only context, we have to do things by hand.
3180 string fullTypeNameToFind = attributeType.FullName;
3182 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3183 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3186 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3188 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
3190 Attribute attr = null;
3192 Debug.Assert(data.ConstructorArguments.Count <= 1);
3194 if (data.ConstructorArguments.Count == 1)
3196 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
3198 else if (data.ConstructorArguments.Count == 0)
3200 attr = (Attribute)Activator.CreateInstance(attributeType);
3205 Type t = attr.GetType();
3207 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3209 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
3210 object value = namedArgument.TypedValue.Value;
3212 if (p.PropertyType.IsEnum)
3214 value = Enum.Parse(p.PropertyType, value.ToString());
3217 p.SetValue(attr, value, null);
3226 #else // ES_BUILD_PCL && ES_BUILD_PN
3227 // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
3228 throw new ArgumentException(Resources.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
3233 /// Evaluates if two related "EventSource"-domain types should be considered the same
3235 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3236 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3237 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3238 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3240 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3241 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3244 // are these the same type?
3245 attributeType == reflectedAttributeType ||
3246 // are the full typenames equal?
3247 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3248 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3249 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3250 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3251 attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
3252 (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
3253 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3254 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
3259 private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3261 // return false for "object" and interfaces
3262 if (eventSourceType.BaseType() == null)
3265 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3268 eventSourceType = eventSourceType.BaseType();
3270 while (eventSourceType != null && eventSourceType.IsAbstract());
3272 if (eventSourceType != null)
3274 if (!allowEventSourceOverride)
3276 if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
3277 !reflectionOnly && eventSourceType != typeof(EventSource))
3282 if (eventSourceType.Name != "EventSource")
3286 return eventSourceType;
3289 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3290 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3291 // at run time. 'source' is the event source to place the descriptors. If it is null,
3292 // then the descriptors are not creaed, and just the manifest is generated.
3293 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
3294 EventManifestOptions flags = EventManifestOptions.None)
3296 ManifestBuilder manifest = null;
3297 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3298 Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3301 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3304 #if DEBUG && ES_BUILD_STANDALONE
3305 TestSupport.TestHooks.MaybeThrow(eventSourceType,
3306 TestSupport.Category.ManifestError,
3307 "EventSource_CreateManifestAndDescriptors",
3308 new ArgumentException("EventSource_CreateManifestAndDescriptors"));
3313 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3314 EventAttribute defaultEventAttribute;
3315 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3316 EventMetadata[] eventData = null;
3317 Dictionary<string, string> eventsByName = null;
3318 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3320 eventData = new EventMetadata[methods.Length + 1];
3321 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3324 // See if we have localization information.
3325 ResourceManager resources = null;
3326 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3327 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3328 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3330 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3333 // Add an entry unconditionally for event ID 0 which will be for a string message.
3334 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3335 manifest.AddEventParameter(typeof(string), "message");
3336 manifest.EndEvent();
3338 // eventSourceType must be sealed and must derive from this EventSource
3339 if ((flags & EventManifestOptions.Strict) != 0)
3341 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3345 manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
3347 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3349 manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
3353 // Collect task, opcode, keyword and channel information
3354 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3355 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3357 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3360 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3361 if (nestedType != null)
3363 if (eventSourceType.IsAbstract())
3365 manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
3369 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3371 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3376 // ensure we have keywords for the session-filtering reserved bits
3378 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3379 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3380 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3381 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3384 if (eventSourceType != typeof(EventSource))
3386 for (int i = 0; i < methods.Length; i++)
3388 MethodInfo method = methods[i];
3389 ParameterInfo[] args = method.GetParameters();
3391 // Get the EventDescriptor (from the Custom attributes)
3392 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3394 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3395 // the only reason of limiting the number of methods considered to be events. This broke a common
3396 // design of having event sources implement specific interfaces. To fix this in a compatible way
3397 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3398 // as they are marked with the [Event] attribute
3399 if (/* method.IsVirtual || */ method.IsStatic)
3404 if (eventSourceType.IsAbstract())
3406 if (eventAttribute != null)
3408 manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
3412 else if (eventAttribute == null)
3414 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3415 // (see Compat comment above)
3416 if (method.ReturnType != typeof(void))
3421 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3422 // (see Compat comment above)
3423 if (method.IsVirtual)
3428 // If we explicitly mark the method as not being an event, then honor that.
3429 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3432 defaultEventAttribute = new EventAttribute(eventId);
3433 eventAttribute = defaultEventAttribute;
3435 else if (eventAttribute.EventId <= 0)
3437 manifest.ManifestError(Resources.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
3438 continue; // don't validate anything else for this event
3440 if (method.Name.LastIndexOf('.') >= 0)
3442 manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
3446 string eventName = method.Name;
3448 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3450 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3451 bool noTask = (eventAttribute.Task == EventTask.None);
3453 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3455 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3456 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3457 if (!eventAttribute.IsOpcodeSet)
3458 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3460 // Make the stop opcode have the same task as the start opcode.
3463 if (eventAttribute.Opcode == EventOpcode.Start)
3465 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3466 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3467 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3469 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3470 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3471 manifest.AddTask(taskName, (int)eventAttribute.Task);
3474 else if (eventAttribute.Opcode == EventOpcode.Stop)
3476 // Find the start associated with this stop event. We require start to be immediately before the stop
3477 int startEventId = eventAttribute.EventId - 1;
3478 if (eventData != null && startEventId < eventData.Length)
3480 Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3481 EventMetadata startEventMetadata = eventData[startEventId];
3483 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3484 // Ideally we would throw an error
3485 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3486 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3487 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3488 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3491 // Make the stop event match the start event
3492 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3496 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3498 throw new ArgumentException(Resources.GetResourceString("EventSource_StopsFollowStarts"));
3504 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3505 if (!(source != null && source.SelfDescribingEvents))
3507 manifest.StartEvent(eventName, eventAttribute);
3508 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3510 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3512 manifest.EndEvent();
3515 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3517 // Do checking for user errors (optional, but not a big deal so we do it).
3518 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3520 #if FEATURE_MANAGED_ETW_CHANNELS
3521 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3522 // and is not required for the manifest
3523 if (eventAttribute.Channel != EventChannel.None)
3527 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3531 string eventKey = "event_" + eventName;
3532 string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3533 // overwrite inline message with the localized message
3534 if (msg != null) eventAttribute.Message = msg;
3536 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3541 // Tell the TraceLogging stuff where to start allocating its own IDs.
3542 NameInfo.ReserveEventIDsBelow(eventId);
3546 TrimEventDescriptors(ref eventData);
3547 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3548 #if FEATURE_MANAGED_ETW_CHANNELS
3549 source.m_channelData = manifest.GetChannelData();
3553 // if this is an abstract event source we've already performed all the validation we can
3554 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3556 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3557 #if FEATURE_MANAGED_ETW_CHANNELS
3558 || manifest.GetChannelData().Length > 0
3562 // if the manifest is not needed and we're not requested to validate the event source return early
3563 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3566 res = manifest.CreateManifest();
3571 // if this is a runtime manifest generation let the exception propagate
3572 if ((flags & EventManifestOptions.Strict) == 0)
3574 // else store it to include it in the Argument exception we raise below
3578 if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3580 string msg = String.Empty;
3581 if (manifest.Errors.Count > 0)
3583 bool firstError = true;
3584 foreach (string error in manifest.Errors)
3587 msg += Environment.NewLine;
3593 msg = "Unexpected error: " + exception.Message;
3595 throw new ArgumentException(msg, exception);
3598 return bNeedsManifest ? res : null;
3601 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3603 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3604 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3605 string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
3607 var newargs = new ParameterInfo[args.Length - 1];
3608 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3617 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3619 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3621 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3622 Type staticFieldType = staticField.FieldType;
3623 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3625 if (providerEnumKind != "Opcodes") goto Error;
3626 int value = (int)staticField.GetRawConstantValue();
3627 manifest.AddOpcode(staticField.Name, value);
3629 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3631 if (providerEnumKind != "Tasks") goto Error;
3632 int value = (int)staticField.GetRawConstantValue();
3633 manifest.AddTask(staticField.Name, value);
3635 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3637 if (providerEnumKind != "Keywords") goto Error;
3638 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3639 manifest.AddKeyword(staticField.Name, value);
3641 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3642 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3644 if (providerEnumKind != "Channels") goto Error;
3645 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3646 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3651 manifest.ManifestError(Resources.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
3654 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3655 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3656 // it is populated if we need to look up message resources
3657 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3658 EventAttribute eventAttribute, ParameterInfo[] eventParameters,
3659 bool hasRelatedActivityID)
3661 if (eventData == null || eventData.Length <= eventAttribute.EventId)
3663 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3664 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3665 eventData = newValues;
3668 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3669 eventAttribute.EventId,
3670 eventAttribute.Version,
3671 #if FEATURE_MANAGED_ETW_CHANNELS
3672 (byte)eventAttribute.Channel,
3676 (byte)eventAttribute.Level,
3677 (byte)eventAttribute.Opcode,
3678 (int)eventAttribute.Task,
3679 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3681 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3682 eventData[eventAttribute.EventId].Name = eventName;
3683 eventData[eventAttribute.EventId].Parameters = eventParameters;
3684 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3685 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3686 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3689 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3690 // size after all event descriptors have been added.
3691 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3693 int idx = eventData.Length;
3697 if (eventData[idx].Descriptor.EventId != 0)
3700 if (eventData.Length - idx > 2) // allow one wasted slot.
3702 EventMetadata[] newValues = new EventMetadata[idx + 1];
3703 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3704 eventData = newValues;
3708 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3709 // when a listener gets attached to a eventSource
3710 internal void AddListener(EventListener listener)
3712 lock (EventListener.EventListenersLock)
3714 bool[] enabledArray = null;
3715 if (m_eventData != null)
3716 enabledArray = new bool[m_eventData.Length];
3717 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3718 listener.OnEventSourceCreated(this);
3722 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3723 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3724 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3725 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3726 ManifestBuilder manifest, EventManifestOptions options)
3728 int evtId = eventAttribute.EventId;
3729 string evtName = method.Name;
3730 int eventArg = GetHelperCallFirstArg(method);
3731 if (eventArg >= 0 && evtId != eventArg)
3733 manifest.ManifestError(Resources.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
3736 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3738 manifest.ManifestError(Resources.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
3741 // We give a task to things if they don't have one.
3742 // TODO this is moderately expensive (N*N). We probably should not even bother....
3743 Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3744 for (int idx = 0; idx < eventData.Length; ++idx)
3746 // skip unused Event IDs.
3747 if (eventData[idx].Name == null)
3750 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3752 manifest.ManifestError(Resources.GetResourceString("EventSource_TaskOpcodePairReused",
3753 evtName, evtId, eventData[idx].Name, idx));
3754 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3755 if ((options & EventManifestOptions.Strict) == 0)
3760 // for non-default event opcodes the user must define a task!
3761 if (eventAttribute.Opcode != EventOpcode.Info)
3763 bool failure = false;
3764 if (eventAttribute.Task == EventTask.None)
3768 // If you have the auto-assigned Task, then you did not explicitly set one.
3769 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3770 // But all other cases we want to catch the omission.
3771 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3772 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3777 manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
3781 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3782 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3783 // by eventRegister.exe)
3784 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3785 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3787 // throw new WarningException(Resources.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
3790 if (eventsByName == null)
3791 eventsByName = new Dictionary<string, string>();
3793 if (eventsByName.ContainsKey(evtName))
3795 manifest.ManifestError(Resources.GetResourceString("EventSource_EventNameReused", evtName), true);
3798 eventsByName[evtName] = evtName;
3802 /// This method looks at the IL and tries to pattern match against the standard
3803 /// 'boilerplate' event body
3805 /// { if (Enabled()) WriteEvent(#, ...) }
3807 /// If the pattern matches, it returns the literal number passed as the first parameter to
3808 /// the WriteEvent. This is used to find common user errors (mismatching this
3809 /// number with the EventAttribute ID). It is only used for validation.
3811 /// <param name="method">The method to probe.</param>
3812 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3813 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3814 static private int GetHelperCallFirstArg(MethodInfo method)
3816 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3817 // Currently searches for the following pattern
3819 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3822 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3824 // NOP // 0 or more times
3827 // If we find this pattern we return the XXX. Otherwise we return -1.
3829 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3831 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3833 for (int idx = 0; idx < instrs.Length;)
3835 switch (instrs[idx])
3858 case 21: // LDC_I4_M1
3859 case 22: // LDC_I4_0
3860 case 23: // LDC_I4_1
3861 case 24: // LDC_I4_2
3862 case 25: // LDC_I4_3
3863 case 26: // LDC_I4_4
3864 case 27: // LDC_I4_5
3865 case 28: // LDC_I4_6
3866 case 29: // LDC_I4_7
3867 case 30: // LDC_I4_8
3868 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3869 retVal = instrs[idx] - 22;
3871 case 31: // LDC_I4_S
3872 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3873 retVal = instrs[idx + 1];
3886 // Is this call just before return?
3887 for (int search = idx + 1; search < instrs.Length; search++)
3889 if (instrs[search] == 42) // RET
3891 if (instrs[search] != 0) // NOP
3897 case 44: // BRFALSE_S
3898 case 45: // BRTRUE_S
3907 case 103: // CONV_I1
3908 case 104: // CONV_I2
3909 case 105: // CONV_I4
3910 case 106: // CONV_I8
3911 case 109: // CONV_U4
3912 case 110: // CONV_U8
3918 case 162: // STELEM_REF
3922 // Covers the CEQ instructions used in debug code for some reason.
3923 if (idx >= instrs.Length || instrs[idx] >= 6)
3927 /* Debug.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3928 " at " + idx + " in method " + method.Name); */
3937 #if false // This routine is not needed at all, it was used for unit test debugging.
3938 [Conditional("DEBUG")]
3939 private static void OutputDebugString(string msg)
3942 msg = msg.TrimEnd('\r', '\n') +
3943 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
3944 System.Diagnostics.Debugger.Log(0, null, msg);
3950 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3951 /// It will do this even if the EventSource is not enabled.
3952 /// TODO remove flush parameter it is not used.
3954 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3955 internal void ReportOutOfBandMessage(string msg, bool flush)
3959 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3960 // send message to debugger without delay
3961 System.Diagnostics.Debugger.Log(0, null, String.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
3964 // Send it to all listeners.
3965 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3966 m_outOfBandMessageCount++;
3969 if (m_outOfBandMessageCount == 16)
3971 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3972 msg = "Reached message limit. End of EventSource error messages.";
3975 WriteEventString(EventLevel.LogAlways, -1, msg);
3976 WriteStringToAllListeners("EventSourceMessage", msg);
3978 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3981 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3983 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3984 EventSourceSettings.EtwSelfDescribingEventFormat;
3985 if ((settings & evtFormatMask) == evtFormatMask)
3987 throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), nameof(settings));
3990 // If you did not explicitly ask for manifest, you get self-describing.
3991 if ((settings & evtFormatMask) == 0)
3992 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3996 private bool ThrowOnEventWriteErrors
3998 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
4001 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
4002 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
4006 private bool SelfDescribingEvents
4010 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
4011 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
4012 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
4018 m_config |= EventSourceSettings.EtwManifestEventFormat;
4019 m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
4023 m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
4024 m_config &= ~EventSourceSettings.EtwManifestEventFormat;
4029 #if FEATURE_ACTIVITYSAMPLING
4030 private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
4032 Debug.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
4034 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
4036 if (!sessions[perEventSourceSessionId])
4040 if (listener == null)
4042 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
4043 Debug.Assert(etwSession != null);
4044 af = etwSession.m_activityFilter;
4048 af = listener.m_activityFilter;
4054 SessionMask m = new SessionMask();
4055 m[perEventSourceSessionId] = true;
4057 foreach (var t in af.GetFilterAsTuple(m_guid))
4059 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
4062 bool participateInSampling = (listener == null) ?
4063 m_activityFilteringForETWEnabled[perEventSourceSessionId] :
4064 GetDispatcher(listener).m_activityFilteringEnabled;
4065 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
4066 perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
4069 #endif // FEATURE_ACTIVITYSAMPLING
4071 // private instance state
4072 private string m_name; // My friendly name (privided in ctor)
4073 internal int m_id; // A small integer that is unique to this instance.
4074 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
4075 internal volatile EventMetadata[] m_eventData; // None per-event data
4076 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
4078 private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
4080 private EventSourceSettings m_config; // configuration information
4082 private bool m_eventSourceDisposed; // has Dispose been called.
4085 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
4086 internal EventLevel m_level; // highest level enabled by any output dispatcher
4087 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
4089 // Dispatching state
4090 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
4091 #if FEATURE_MANAGED_ETW
4092 private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
4094 private bool m_completelyInited; // The EventSource constructor has returned without exception.
4095 private Exception m_constructionException; // If there was an exception construction, this is it
4096 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
4097 private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
4099 private string[] m_traits; // Used to implement GetTraits
4101 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
4103 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
4106 private static bool m_EventSourceInDecodeObject = false;
4108 #if FEATURE_MANAGED_ETW_CHANNELS
4109 internal volatile ulong[] m_channelData;
4112 #if FEATURE_ACTIVITYSAMPLING
4113 private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
4114 private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
4115 private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
4116 internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
4117 internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
4118 static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
4119 // Also used to mark that activity tracing is on for some case
4120 #endif // FEATURE_ACTIVITYSAMPLING
4122 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
4123 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
4124 ActivityTracker m_activityTracker;
4125 internal const string s_ActivityStartSuffix = "Start";
4126 internal const string s_ActivityStopSuffix = "Stop";
4128 // used for generating GUID from eventsource name
4129 private static readonly byte[] namespaceBytes = new byte[] {
4130 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
4131 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
4138 /// Enables specifying event source configuration options to be used in the EventSource constructor.
4141 public enum EventSourceSettings
4144 /// This specifies none of the special configuration options should be enabled.
4148 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
4150 ThrowOnEventWriteErrors = 1,
4152 /// Setting this option is a directive to the ETW listener should use manifest-based format when
4153 /// firing events. This is the default option when defining a type derived from EventSource
4154 /// (using the protected EventSource constructors).
4155 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4157 EtwManifestEventFormat = 4,
4159 /// Setting this option is a directive to the ETW listener should use self-describing event format
4160 /// when firing events. This is the default option when creating a new instance of the EventSource
4161 /// type (using the public EventSource constructors).
4162 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4164 EtwSelfDescribingEventFormat = 8,
4168 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
4169 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
4170 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
4171 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
4172 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
4173 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
4176 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
4177 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
4178 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
4179 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
4181 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
4182 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
4184 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4185 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4186 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4187 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4188 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4190 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4191 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4192 /// that eventSource what events that dispatcher will receive.
4194 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4195 /// override this method to do something useful with the data.
4197 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4198 /// invariant associated with this callback is that every eventSource gets exactly one
4199 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4200 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4201 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4205 public class EventListener : IDisposable
4207 private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
4210 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4211 /// This can happen for all existing EventSources when the EventListener is created
4212 /// as well as for any EventSources that come into existence after the EventListener
4213 /// has been created.
4215 /// These 'catch up' events are called during the construction of the EventListener.
4216 /// Subclasses need to be prepared for that.
4218 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4219 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4221 public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
4225 CallBackForExistingEventSources(false, value);
4227 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
4231 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
4236 /// This event is raised whenever an event has been written by a EventSource for which
4237 /// the EventListener has enabled events.
4239 public event EventHandler<EventWrittenEventArgs> EventWritten;
4242 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4245 public EventListener()
4247 // This will cause the OnEventSourceCreated callback to fire.
4248 CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener((EventListener)obj));
4252 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4253 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4254 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4255 /// call Dispose when they are done with their logging.
4257 #if ES_BUILD_STANDALONE
4258 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4260 public virtual void Dispose()
4262 lock (EventListenersLock)
4264 if (s_Listeners != null)
4266 if (this == s_Listeners)
4268 EventListener cur = s_Listeners;
4269 s_Listeners = this.m_Next;
4270 RemoveReferencesToListenerInEventSources(cur);
4274 // Find 'this' from the s_Listeners linked list.
4275 EventListener prev = s_Listeners;
4278 EventListener cur = prev.m_Next;
4283 // Found our Listener, remove references to to it in the eventSources
4284 prev.m_Next = cur.m_Next; // Remove entry.
4285 RemoveReferencesToListenerInEventSources(cur);
4295 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4296 // 'cleanup' associated with this object
4299 /// Enable all events from the eventSource identified by 'eventSource' to the current
4300 /// dispatcher that have a verbosity level of 'level' or lower.
4302 /// This call can have the effect of REDUCING the number of events sent to the
4303 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4305 /// This call never has an effect on other EventListeners.
4308 public void EnableEvents(EventSource eventSource, EventLevel level)
4310 EnableEvents(eventSource, level, EventKeywords.None);
4313 /// Enable all events from the eventSource identified by 'eventSource' to the current
4314 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4315 /// matching any of the bits in 'matchAnyKeyword'.
4317 /// This call can have the effect of REDUCING the number of events sent to the
4318 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4319 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4321 /// This call never has an effect on other EventListeners.
4323 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4325 EnableEvents(eventSource, level, matchAnyKeyword, null);
4328 /// Enable all events from the eventSource identified by 'eventSource' to the current
4329 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4330 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4331 /// effect passing additional 'key-value' arguments 'arguments' might have.
4333 /// This call can have the effect of REDUCING the number of events sent to the
4334 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4335 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4337 /// This call never has an effect on other EventListeners.
4339 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
4341 if (eventSource == null)
4343 throw new ArgumentNullException(nameof(eventSource));
4345 Contract.EndContractBlock();
4347 eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4350 /// Disables all events coming from eventSource identified by 'eventSource'.
4352 /// This call never has an effect on other EventListeners.
4354 public void DisableEvents(EventSource eventSource)
4356 if (eventSource == null)
4358 throw new ArgumentNullException(nameof(eventSource));
4360 Contract.EndContractBlock();
4362 eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4366 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4367 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4368 /// it useful to store additional information about each eventSource connected to it,
4369 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4370 /// (growable) array (eg List(T)).
4372 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4375 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4376 /// This can happen for all existing EventSources when the EventListener is created
4377 /// as well as for any EventSources that come into existence after the EventListener
4378 /// has been created.
4380 /// These 'catch up' events are called during the construction of the EventListener.
4381 /// Subclasses need to be prepared for that.
4383 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4384 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4386 /// <param name="eventSource"></param>
4387 internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4389 EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
4390 if (callBack != null)
4392 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4393 args.EventSource = eventSource;
4394 callBack(this, args);
4399 /// This method is called whenever an event has been written by a EventSource for which
4400 /// the EventListener has enabled events.
4402 /// <param name="eventData"></param>
4403 internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4405 EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
4406 if (callBack != null)
4408 callBack(this, eventData);
4415 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4416 /// ID to the eventSource (which is simply the ordinal in the global list).
4418 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4419 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4420 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4421 /// that are in the list). This seems OK since the expectation is that EventSources
4422 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4423 /// global variables).
4425 /// <param name="newEventSource"></param>
4426 internal static void AddEventSource(EventSource newEventSource)
4428 lock (EventListenersLock)
4430 if (s_EventSources == null)
4431 s_EventSources = new List<WeakReference>(2);
4433 if (!s_EventSourceShutdownRegistered)
4435 s_EventSourceShutdownRegistered = true;
4439 // Periodically search the list for existing entries to reuse, this avoids
4440 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4442 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4444 int i = s_EventSources.Count; // Work from the top down.
4448 WeakReference weakRef = s_EventSources[i];
4449 if (!weakRef.IsAlive)
4452 weakRef.Target = newEventSource;
4459 newIndex = s_EventSources.Count;
4460 s_EventSources.Add(new WeakReference(newEventSource));
4462 newEventSource.m_id = newIndex;
4464 // Add every existing dispatcher to the new EventSource
4465 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4466 newEventSource.AddListener(listener);
4472 // Whenver we have async callbacks from native code, there is an ugly issue where
4473 // during .NET shutdown native code could be calling the callback, but the CLR
4474 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4475 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4476 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4477 // do this. This is what this callback is for.
4478 // See bug 724140 for more
4479 private static void DisposeOnShutdown(object sender, EventArgs e)
4481 lock (EventListenersLock)
4483 foreach (var esRef in s_EventSources)
4485 EventSource es = esRef.Target as EventSource;
4493 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4494 /// eventSources in the appdomain.
4496 /// The EventListenersLock must be held before calling this routine.
4498 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4500 #if !ES_BUILD_STANDALONE
4501 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4503 // Foreach existing EventSource in the appdomain
4504 foreach (WeakReference eventSourceRef in s_EventSources)
4506 EventSource eventSource = eventSourceRef.Target as EventSource;
4507 if (eventSource != null)
4509 // Is the first output dispatcher the dispatcher we are removing?
4510 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4511 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4514 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4515 EventDispatcher prev = eventSource.m_Dispatchers;
4518 EventDispatcher cur = prev.m_Next;
4521 Debug.Assert(false, "EventSource did not have a registered EventListener!");
4524 if (cur.m_Listener == listenerToRemove)
4526 prev.m_Next = cur.m_Next; // Remove entry.
4537 /// Checks internal consistency of EventSources/Listeners.
4539 [Conditional("DEBUG")]
4540 internal static void Validate()
4542 lock (EventListenersLock)
4544 // Get all listeners
4545 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4546 EventListener cur = s_Listeners;
4549 allListeners.Add(cur, true);
4553 // For all eventSources
4555 foreach (WeakReference eventSourceRef in s_EventSources)
4558 EventSource eventSource = eventSourceRef.Target as EventSource;
4559 if (eventSource == null)
4561 Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4563 // None listeners on eventSources exist in the dispatcher list.
4564 EventDispatcher dispatcher = eventSource.m_Dispatchers;
4565 while (dispatcher != null)
4567 Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4568 dispatcher = dispatcher.m_Next;
4571 // Every dispatcher is on Dispatcher List of every eventSource.
4572 foreach (EventListener listener in allListeners.Keys)
4574 dispatcher = eventSource.m_Dispatchers;
4577 Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4578 if (dispatcher.m_Listener == listener)
4580 dispatcher = dispatcher.m_Next;
4588 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4589 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4590 /// the lock object)
4592 internal static object EventListenersLock
4596 if (s_EventSources == null)
4597 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4598 return s_EventSources;
4602 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
4604 lock (EventListenersLock)
4606 // Disallow creating EventListener reentrancy.
4607 if (s_CreatingListener)
4609 throw new InvalidOperationException(Resources.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
4614 s_CreatingListener = true;
4616 if (addToListenersList)
4618 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4619 // Those added sources see this listener.
4620 this.m_Next = s_Listeners;
4624 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4625 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4626 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4627 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4629 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4631 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4633 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4634 EventSource eventSource = eventSourceRef.Target as EventSource;
4635 if (eventSource != null)
4637 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4638 args.EventSource = eventSource;
4639 callback(this, args);
4647 s_CreatingListener = false;
4654 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
4655 #if FEATURE_ACTIVITYSAMPLING
4656 internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
4657 #endif // FEATURE_ACTIVITYSAMPLING
4662 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4663 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4665 internal static EventListener s_Listeners;
4667 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4668 /// remove themselves from this list this is a weak list and the GC that removes them may
4669 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4670 /// to filter those out.
4672 internal static List<WeakReference> s_EventSources;
4675 /// Used to disallow reentrancy.
4677 private static bool s_CreatingListener = false;
4680 /// Used to register AD/Process shutdown callbacks.
4682 private static bool s_EventSourceShutdownRegistered = false;
4687 /// Passed to the code:EventSource.OnEventCommand callback
4689 public class EventCommandEventArgs : EventArgs
4692 /// Gets the command for the callback.
4694 public EventCommand Command { get; internal set; }
4697 /// Gets the arguments for the callback.
4699 public IDictionary<String, String> Arguments { get; internal set; }
4702 /// Enables the event that has the specified identifier.
4704 /// <param name="eventId">Event ID of event to be enabled</param>
4705 /// <returns>true if eventId is in range</returns>
4706 public bool EnableEvent(int eventId)
4708 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4709 throw new InvalidOperationException();
4710 return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
4714 /// Disables the event that have the specified identifier.
4716 /// <param name="eventId">Event ID of event to be disabled</param>
4717 /// <returns>true if eventId is in range</returns>
4718 public bool DisableEvent(int eventId)
4720 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4721 throw new InvalidOperationException();
4722 return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
4727 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4728 EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4730 this.Command = command;
4731 this.Arguments = arguments;
4732 this.eventSource = eventSource;
4733 this.listener = listener;
4734 this.perEventSourceSessionId = perEventSourceSessionId;
4735 this.etwSessionId = etwSessionId;
4736 this.enable = enable;
4738 this.matchAnyKeyword = matchAnyKeyword;
4741 internal EventSource eventSource;
4742 internal EventDispatcher dispatcher;
4744 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4745 internal EventListener listener;
4746 internal int perEventSourceSessionId;
4747 internal int etwSessionId;
4748 internal bool enable;
4749 internal EventLevel level;
4750 internal EventKeywords matchAnyKeyword;
4751 internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
4757 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4759 public class EventSourceCreatedEventArgs : EventArgs
4762 /// The EventSource that is attaching to the listener.
4764 public EventSource EventSource
4772 /// EventWrittenEventArgs is passed to the user-provided override for
4773 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4775 public class EventWrittenEventArgs : EventArgs
4778 /// The name of the event.
4780 public string EventName
4784 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4789 return m_eventSource.m_eventData[EventId].Name;
4793 m_eventName = value;
4798 /// Gets the event ID for the event that was written.
4800 public int EventId { get; internal set; }
4803 /// Gets the activity ID for the thread on which the event was written.
4805 public Guid ActivityId
4807 get { return EventSource.CurrentThreadActivityId; }
4811 /// Gets the related activity ID if one was specified when the event was written.
4813 public Guid RelatedActivityId
4820 /// Gets the payload for the event.
4822 public ReadOnlyCollection<Object> Payload { get; internal set; }
4825 /// Gets the payload argument names.
4827 public ReadOnlyCollection<string> PayloadNames
4831 // For contract based events we create the list lazily.
4832 if (m_payloadNames == null)
4834 // Self described events are identified by id -1.
4835 Debug.Assert(EventId != -1);
4837 var names = new List<string>();
4838 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4840 names.Add(parameter.Name);
4842 m_payloadNames = new ReadOnlyCollection<string>(names);
4845 return m_payloadNames;
4850 m_payloadNames = value;
4855 /// Gets the event source object.
4857 public EventSource EventSource { get { return m_eventSource; } }
4860 /// Gets the keywords for the event.
4862 public EventKeywords Keywords
4866 if (EventId < 0) // TraceLogging convention EventID == -1
4869 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4874 /// Gets the operation code for the event.
4876 public EventOpcode Opcode
4880 if (EventId <= 0) // TraceLogging convention EventID == -1
4882 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4887 /// Gets the task for the event.
4889 public EventTask Task
4893 if (EventId <= 0) // TraceLogging convention EventID == -1
4894 return EventTask.None;
4896 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4901 /// Any provider/user defined options associated with the event.
4903 public EventTags Tags
4907 if (EventId <= 0) // TraceLogging convention EventID == -1
4909 return m_eventSource.m_eventData[EventId].Tags;
4914 /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
4916 public string Message
4920 if (EventId <= 0) // TraceLogging convention EventID == -1
4923 return m_eventSource.m_eventData[EventId].Message;
4932 #if FEATURE_MANAGED_ETW_CHANNELS
4934 /// Gets the channel for the event.
4936 public EventChannel Channel
4940 if (EventId <= 0) // TraceLogging convention EventID == -1
4941 return EventChannel.None;
4942 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4948 /// Gets the version of the event.
4954 if (EventId <= 0) // TraceLogging convention EventID == -1
4956 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4961 /// Gets the level for the event.
4963 public EventLevel Level
4967 if (EventId <= 0) // TraceLogging convention EventID == -1
4969 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4974 internal EventWrittenEventArgs(EventSource eventSource)
4976 m_eventSource = eventSource;
4978 private string m_message;
4979 private string m_eventName;
4980 private EventSource m_eventSource;
4981 private ReadOnlyCollection<string> m_payloadNames;
4982 internal EventTags m_tags;
4983 internal EventOpcode m_opcode;
4984 internal EventLevel m_level;
4985 internal EventKeywords m_keywords;
4990 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4992 [AttributeUsage(AttributeTargets.Class)]
4993 public sealed class EventSourceAttribute : Attribute
4996 /// Overrides the ETW name of the event source (which defaults to the class name)
4998 public string Name { get; set; }
5001 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
5002 /// except when upgrading existing ETW providers to using event sources.
5004 public string Guid { get; set; }
5008 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
5009 /// can be localized to several languages if desired. This works by creating a ResX style string table
5010 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
5011 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
5012 /// resources. This name is the value of the LocalizationResources property.
5014 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
5015 /// using the following resource naming scheme
5017 /// <para>* event_EVENTNAME</para>
5018 /// <para>* task_TASKNAME</para>
5019 /// <para>* keyword_KEYWORDNAME</para>
5020 /// <para>* map_MAPNAME</para>
5022 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
5023 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
5024 /// which represent the payload values.
5027 public string LocalizationResources { get; set; }
5031 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5032 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5033 /// name of the method and its signature to generate basic schema information for the event. The
5034 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5037 [AttributeUsage(AttributeTargets.Method)]
5038 public sealed class EventAttribute : Attribute
5040 /// <summary>Construct an EventAttribute with specified eventId</summary>
5041 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
5042 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5043 /// <summary>Event's ID</summary>
5044 public int EventId { get; private set; }
5045 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5046 public EventLevel Level { get; set; }
5047 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5048 public EventKeywords Keywords { get; set; }
5049 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5050 public EventOpcode Opcode
5058 this.m_opcode = value;
5059 this.m_opcodeSet = true;
5063 internal bool IsOpcodeSet
5071 /// <summary>Event's task: allows logical grouping of events</summary>
5072 public EventTask Task { get; set; }
5073 #if FEATURE_MANAGED_ETW_CHANNELS
5074 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5075 public EventChannel Channel { get; set; }
5077 /// <summary>Event's version</summary>
5078 public byte Version { get; set; }
5081 /// This can be specified to enable formatting and localization of the event's payload. You can
5082 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5083 /// with the 'ToString()' of the corresponding part of the event payload.
5085 public string Message { get; set; }
5088 /// User defined options associated with the event. These do not have meaning to the EventSource but
5089 /// are passed through to listeners which given them semantics.
5091 public EventTags Tags { get; set; }
5094 /// Allows fine control over the Activity IDs generated by start and stop events
5096 public EventActivityOptions ActivityOptions { get; set; }
5099 EventOpcode m_opcode;
5100 private bool m_opcodeSet;
5105 /// By default all instance methods in a class that subclasses code:EventSource that and return
5106 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5107 /// the code:NonEventAttribute
5109 [AttributeUsage(AttributeTargets.Method)]
5110 public sealed class NonEventAttribute : Attribute
5113 /// Constructs a default NonEventAttribute
5115 public NonEventAttribute() { }
5118 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5119 #if FEATURE_MANAGED_ETW_CHANNELS
5121 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5122 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5124 /// public static class Channels
5126 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5127 /// public const EventChannel Admin = (EventChannel)16;
5129 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5130 /// public const EventChannel Operational = (EventChannel)17;
5134 [AttributeUsage(AttributeTargets.Field)]
5135 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5138 class EventChannelAttribute : Attribute
5141 /// Specified whether the channel is enabled by default
5143 public bool Enabled { get; set; }
5146 /// Legal values are in EventChannelType
5148 public EventChannelType EventChannelType { get; set; }
5150 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5152 /// Specifies the isolation for the channel
5154 public EventChannelIsolation Isolation { get; set; }
5157 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5158 /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
5160 public string Access { get; set; }
5163 /// Allows importing channels defined in external manifests
5165 public string ImportChannel { get; set; }
5168 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
5169 // public string Name { get; set; }
5173 /// Allowed channel types
5175 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5178 enum EventChannelType
5180 /// <summary>The admin channel</summary>
5182 /// <summary>The operational channel</summary>
5184 /// <summary>The Analytic channel</summary>
5186 /// <summary>The debug channel</summary>
5190 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5192 /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
5193 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5194 /// access permissions for the channel and backing file.
5197 enum EventChannelIsolation
5200 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5204 /// All channels that specify System isolation use the same ETW session
5208 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5209 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5210 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5218 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5220 public enum EventCommand
5223 /// Update EventSource state
5227 /// Request EventSource to generate and send its manifest
5241 #region private classes
5243 #if FEATURE_ACTIVITYSAMPLING
5246 /// ActivityFilter is a helper structure that is used to keep track of run-time state
5247 /// associated with activity filtering. It is 1-1 with EventListeners (logically
5248 /// every listener has one of these, however we actually allocate them lazily), as well
5249 /// as 1-to-1 with tracing-aware EtwSessions.
5251 /// This structure also keeps track of the sampling counts associated with 'trigger'
5252 /// events. Because these trigger events are rare, and you typically only have one of
5253 /// them, we store them here as a linked list.
5255 internal sealed class ActivityFilter : IDisposable
5258 /// Disable all activity filtering for the listener associated with 'filterList',
5259 /// (in the session associated with it) that is triggered by any event in 'source'.
5261 public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
5263 #if !ES_BUILD_STANDALONE
5264 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5267 if (filterList == null)
5271 // Remove it from anywhere in the list (except the first element, which has to
5272 // be treated specially)
5273 ActivityFilter prev = filterList;
5277 if (cur.m_providerGuid == source.Guid)
5279 // update TriggersActivityTracking bit
5280 if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
5281 --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
5283 // Remove it from the linked list.
5284 prev.m_next = cur.m_next;
5285 // dispose of the removed node
5298 // Sadly we have to treat the first element specially in linked list removal in C#
5299 if (filterList.m_providerGuid == source.Guid)
5301 // update TriggersActivityTracking bit
5302 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
5303 --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
5305 // We are the first element in the list.
5306 var first = filterList;
5307 filterList = first.m_next;
5308 // dispose of the removed node
5311 // the above might have removed the one ActivityFilter in the session that contains the
5312 // cleanup delegate; re-create the delegate if needed
5313 if (filterList != null)
5315 EnsureActivityCleanupDelegate(filterList);
5320 /// Currently this has "override" semantics. We first disable all filters
5321 /// associated with 'source', and next we add new filters for each entry in the
5322 /// string 'startEvents'. participateInSampling specifies whether non-startEvents
5323 /// always trigger or only trigger when current activity is 'active'.
5325 public static void UpdateFilter(
5326 ref ActivityFilter filterList,
5328 int perEventSourceSessionId,
5331 #if !ES_BUILD_STANDALONE
5332 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5335 // first remove all filters associated with 'source'
5336 DisableFilter(ref filterList, source);
5338 if (!string.IsNullOrEmpty(startEvents))
5340 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
5341 // The Event may be specified by name or by ID. Errors in parsing such a pair
5342 // result in the error being reported to the listeners, and the pair being ignored.
5343 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
5344 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
5345 // we should initiate activity tracing once every 10 events.
5346 string[] activityFilterStrings = startEvents.Split(' ');
5348 for (int i = 0; i < activityFilterStrings.Length; ++i)
5350 string activityFilterString = activityFilterStrings[i];
5353 int colonIdx = activityFilterString.IndexOf(':');
5356 source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
5357 activityFilterString, false);
5358 // ignore failure...
5361 string sFreq = activityFilterString.Substring(colonIdx + 1);
5362 if (!int.TryParse(sFreq, out sampleFreq))
5364 source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
5367 activityFilterString = activityFilterString.Substring(0, colonIdx);
5368 if (!int.TryParse(activityFilterString, out eventId))
5372 // see if it's an event name
5373 for (int j = 0; j < source.m_eventData.Length; j++)
5375 EventSource.EventMetadata[] ed = source.m_eventData;
5376 if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
5377 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
5379 eventId = ed[j].Descriptor.EventId;
5384 if (eventId < 0 || eventId >= source.m_eventData.Length)
5386 source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
5389 EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
5395 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
5397 public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
5399 for (var af = filterList; af != null; af = af.m_next)
5401 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
5408 /// Returns a session mask representing all sessions in which the activity
5409 /// associated with the current thread is allowed through the activity filter.
5410 /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
5411 /// most of the time this is false as you can guarentee this event is NOT a
5412 /// triggering event. If 'triggeringEvent' is true, then it checks the
5413 /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
5414 /// a trigger. If so it activates the current activity.
5416 /// If 'childActivityID' is present, it will be added to the active set if the
5417 /// current activity is active.
5419 unsafe public static bool PassesActivityFilter(
5420 ActivityFilter filterList,
5421 Guid* childActivityID,
5422 bool triggeringEvent,
5426 Debug.Assert(filterList != null && filterList.m_activeActivities != null);
5427 bool shouldBeLogged = false;
5428 if (triggeringEvent)
5430 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5432 if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
5434 // Update the sampling count with wrap-around
5435 int curSampleCount, newSampleCount;
5438 curSampleCount = af.m_curSampleCount;
5439 if (curSampleCount <= 1)
5440 newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
5442 newSampleCount = curSampleCount - 1;
5444 while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
5445 // If we hit zero, then start tracking the activity.
5446 if (curSampleCount <= 1)
5448 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5449 Tuple<Guid, int> startId;
5450 // only add current activity if it's not already a root activity
5451 if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
5453 // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
5454 shouldBeLogged = true;
5455 af.m_activeActivities[currentActivityId] = Environment.TickCount;
5456 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
5461 // a start event following a triggering start event
5462 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5463 Tuple<Guid, int> startId;
5464 // only remove current activity if we added it
5465 if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
5466 startId.Item1 == source.Guid && startId.Item2 == eventId)
5468 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
5469 // remove activity only from current logging scope (af)
5471 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
5479 var activeActivities = GetActiveActivities(filterList);
5480 if (activeActivities != null)
5482 // if we hadn't already determined this should be logged, test further
5483 if (!shouldBeLogged)
5485 shouldBeLogged = !activeActivities.IsEmpty &&
5486 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
5488 if (shouldBeLogged && childActivityID != null &&
5489 ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
5491 FlowActivityIfNeeded(filterList, null, childActivityID);
5492 // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
5495 // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
5496 return shouldBeLogged;
5499 public static bool IsCurrentActivityActive(ActivityFilter filterList)
5501 var activeActivities = GetActiveActivities(filterList);
5502 if (activeActivities != null &&
5503 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
5510 /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
5511 /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
5512 /// value for 'currentActivityid' is an indication tha caller has already verified
5513 /// that the current activity is active.
5515 unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
5517 Debug.Assert(childActivityID != null);
5519 var activeActivities = GetActiveActivities(filterList);
5520 Debug.Assert(activeActivities != null);
5522 // take currentActivityId == null to mean we *know* the current activity is "active"
5523 if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
5526 if (activeActivities.Count > MaxActivityTrackCount)
5528 TrimActiveActivityStore(activeActivities);
5529 // make sure current activity is still in the set:
5530 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
5532 // add child activity to list of actives
5533 activeActivities[*childActivityID] = Environment.TickCount;
5539 public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
5541 for (var af = activityFilter; af != null; af = af.m_next)
5543 if ((sourceGuid == af.m_providerGuid) &&
5544 (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
5545 ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
5547 // we could be more precise here, if we tracked 'anykeywords' per session
5550 source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
5557 /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
5558 /// associated with 'this' ActivityFilter list, return configured sequence of
5559 /// [eventId, sampleFreq] pairs that defines the sampling policy.
5561 public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
5563 for (ActivityFilter af = this; af != null; af = af.m_next)
5565 if (af.m_providerGuid == sourceGuid)
5566 yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
5571 /// The cleanup being performed consists of removing the m_myActivityDelegate from
5572 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
5574 public void Dispose()
5576 #if !ES_BUILD_STANDALONE
5577 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5579 // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
5580 // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
5581 // during the finalization of the ActivityFilter
5582 if (m_myActivityDelegate != null)
5584 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
5585 m_myActivityDelegate = null;
5592 /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
5593 /// 'samplingFreq' times the event fires. You can have several of these forming a
5596 private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
5598 m_providerGuid = source.Guid;
5599 m_perEventSourceSessionId = perEventSourceSessionId;
5600 m_eventId = eventId;
5601 m_samplingFreq = samplingFreq;
5602 m_next = existingFilter;
5604 Debug.Assert(existingFilter == null ||
5605 (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
5607 // if this is the first filter we add for this session, we need to create a new
5608 // table of activities. m_activeActivities is common across EventSources in the same
5610 ConcurrentDictionary<Guid, int> activeActivities = null;
5611 if (existingFilter == null ||
5612 (activeActivities = GetActiveActivities(existingFilter)) == null)
5614 m_activeActivities = new ConcurrentDictionary<Guid, int>();
5615 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
5617 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
5618 m_myActivityDelegate = GetActivityDyingDelegate(this);
5619 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
5623 m_activeActivities = activeActivities;
5624 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
5630 /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
5631 /// activity-removing delegate for the listener/session associated with 'filterList'.
5633 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
5635 if (filterList == null)
5638 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5640 if (af.m_myActivityDelegate != null)
5644 // we didn't find a delegate
5645 filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
5646 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
5650 /// Builds the delegate to be called when an activity is dying. This is responsible
5651 /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
5652 /// This gets "added" to EventSource.s_activityDying and ends up being called from
5653 /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
5655 /// <returns>The delegate to be called when an activity is dying</returns>
5656 private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
5658 return (Guid oldActivity) =>
5661 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
5662 Tuple<Guid, int> dummyTuple;
5663 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
5668 /// Enables activity filtering for the listener associated with 'filterList', triggering on
5669 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
5671 /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
5672 /// activitySampling if something else triggered).
5674 /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
5675 private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
5677 #if !ES_BUILD_STANDALONE
5678 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5680 Debug.Assert(samplingFreq > 0);
5681 Debug.Assert(eventId >= 0);
5683 filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
5685 // Mark the 'quick Check' that indicates this is a trigger event.
5686 // If eventId is out of range then this mark is not done which has the effect of ignoring
5688 if (0 <= eventId && eventId < source.m_eventData.Length)
5689 ++source.m_eventData[eventId].TriggersActivityTracking;
5695 /// Normally this code never runs, it is here just to prevent run-away resource usage.
5697 private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
5699 if (activities.Count > MaxActivityTrackCount)
5701 // Remove half of the oldest activity ids.
5702 var keyValues = activities.ToArray();
5703 var tickNow = Environment.TickCount;
5705 // Sort by age, taking into account wrap-around. As long as x and y are within
5706 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
5707 // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
5708 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
5709 for (int i = 0; i < keyValues.Length / 2; i++)
5712 activities.TryRemove(keyValues[i].Key, out dummy);
5717 private static ConcurrentDictionary<Guid, int> GetActiveActivities(
5718 ActivityFilter filterList)
5720 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5722 if (af.m_activeActivities != null)
5723 return af.m_activeActivities;
5728 // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
5729 // in the m_next list. The 'int' value in the m_activities set is a timestamp
5730 // (Environment.TickCount) of when the entry was put in the system and is used to
5731 // remove 'old' entries that if the set gets too big.
5732 ConcurrentDictionary<Guid, int> m_activeActivities;
5734 // m_rootActiveActivities holds the "root" active activities, i.e. the activities
5735 // that were marked as active because a Start event fired on them. We need to keep
5736 // track of these to enable sampling in the scenario of an app's main thread that
5737 // never explicitly sets distinct activity IDs as it executes. To handle these
5738 // situations we manufacture a Guid from the thread's ID, and:
5739 // (a) we consider the firing of a start event when the sampling counter reaches
5740 // zero to mark the beginning of an interesting activity, and
5741 // (b) we consider the very next firing of the same start event to mark the
5742 // ending of that activity.
5743 // We use a ConcurrentDictionary to avoid taking explicit locks.
5744 // The key (a guid) represents the activity ID of the root active activity
5745 // The value is made up of the Guid of the event provider and the eventId of
5747 ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
5748 Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
5749 int m_eventId; // triggering event
5750 int m_samplingFreq; // Counter reset to this when it hits 0
5751 int m_curSampleCount; // We count down to 0 and then activate the activity.
5752 int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
5754 const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
5756 ActivityFilter m_next; // We create a linked list of these
5757 Action<Guid> m_myActivityDelegate;
5763 /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
5764 /// are limited to 8 concurrent sessions per machine (currently) we're going to store
5765 /// the active ones in a singly linked list.
5767 internal class EtwSession
5769 public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
5771 if (etwSessionId < 0)
5774 EtwSession etwSession;
5775 foreach (var wrEtwSession in s_etwSessions)
5777 #if ES_BUILD_STANDALONE
5778 if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
5781 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
5786 if (!bCreateIfNeeded)
5789 #if ES_BUILD_STANDALONE
5790 if (s_etwSessions == null)
5791 s_etwSessions = new List<WeakReference>();
5793 etwSession = new EtwSession(etwSessionId);
5794 s_etwSessions.Add(new WeakReference(etwSession));
5796 if (s_etwSessions == null)
5797 s_etwSessions = new List<WeakReference<EtwSession>>();
5799 etwSession = new EtwSession(etwSessionId);
5800 s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
5803 if (s_etwSessions.Count > s_thrSessionCount)
5810 public static void RemoveEtwSession(EtwSession etwSession)
5812 Debug.Assert(etwSession != null);
5813 if (s_etwSessions == null || etwSession == null)
5816 s_etwSessions.RemoveAll((wrEtwSession) =>
5819 #if ES_BUILD_STANDALONE
5820 return (session = (EtwSession) wrEtwSession.Target) != null &&
5821 (session.m_etwSessionId == etwSession.m_etwSessionId);
5823 return wrEtwSession.TryGetTarget(out session) &&
5824 (session.m_etwSessionId == etwSession.m_etwSessionId);
5828 if (s_etwSessions.Count > s_thrSessionCount)
5832 private static void TrimGlobalList()
5834 if (s_etwSessions == null)
5837 s_etwSessions.RemoveAll((wrEtwSession) =>
5839 #if ES_BUILD_STANDALONE
5840 return wrEtwSession.Target == null;
5843 return !wrEtwSession.TryGetTarget(out session);
5848 private EtwSession(int etwSessionId)
5850 m_etwSessionId = etwSessionId;
5853 public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
5854 public ActivityFilter m_activityFilter; // all filters enabled for this session
5856 #if ES_BUILD_STANDALONE
5857 private static List<WeakReference> s_etwSessions = new List<WeakReference>();
5859 private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
5861 private const int s_thrSessionCount = 16;
5864 #endif // FEATURE_ACTIVITYSAMPLING
5866 // holds a bitfield representing a session mask
5868 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5869 /// is the index in the SessionMask of the bit that will be set. These can translate to
5870 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5871 /// FromEventKeywords() methods.
5873 internal struct SessionMask
5875 public SessionMask(SessionMask m)
5876 { m_mask = m.m_mask; }
5878 public SessionMask(uint mask = 0)
5879 { m_mask = mask & MASK; }
5881 public bool IsEqualOrSupersetOf(SessionMask m)
5883 return (this.m_mask | m.m_mask) == this.m_mask;
5886 public static SessionMask All
5888 get { return new SessionMask(MASK); }
5891 public static SessionMask FromId(int perEventSourceSessionId)
5893 Debug.Assert(perEventSourceSessionId < MAX);
5894 return new SessionMask((uint)1 << perEventSourceSessionId);
5897 public ulong ToEventKeywords()
5899 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5902 public static SessionMask FromEventKeywords(ulong m)
5904 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5907 public bool this[int perEventSourceSessionId]
5911 Debug.Assert(perEventSourceSessionId < MAX);
5912 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5916 Debug.Assert(perEventSourceSessionId < MAX);
5917 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5918 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5922 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5924 return new SessionMask(m1.m_mask | m2.m_mask);
5927 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5929 return new SessionMask(m1.m_mask & m2.m_mask);
5932 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5934 return new SessionMask(m1.m_mask ^ m2.m_mask);
5937 public static SessionMask operator ~(SessionMask m)
5939 return new SessionMask(MASK & ~(m.m_mask));
5942 public static explicit operator ulong(SessionMask m)
5943 { return m.m_mask; }
5945 public static explicit operator uint(SessionMask m)
5946 { return m.m_mask; }
5948 private uint m_mask;
5950 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5951 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5952 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5956 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5957 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5959 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5960 /// that that EventListener has activate) and a Single EventSource may also have many
5961 /// event Dispatchers (one for every EventListener that has activated it).
5963 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5964 /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
5965 /// associated with.
5967 internal class EventDispatcher
5969 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
5972 m_EventEnabled = eventEnabled;
5973 m_Listener = listener;
5977 readonly internal EventListener m_Listener; // The dispatcher this entry is for
5978 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
5979 #if FEATURE_ACTIVITYSAMPLING
5980 internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
5981 #endif // FEATURE_ACTIVITYSAMPLING
5983 // Only guaranteed to exist after a InsureInit()
5984 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5985 // Of all listeners for that eventSource.
5989 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5993 public enum EventManifestOptions
5996 /// Only the resources associated with current UI culture are included in the manifest
6000 /// Throw exceptions for any inconsistency encountered
6004 /// Generate a "resources" node under "localization" for every satellite assembly provided
6008 /// Generate the manifest only if the event source needs to be registered on the machine,
6009 /// otherwise return null (but still perform validation if Strict is specified)
6011 OnlyIfNeededForRegistration = 0x4,
6013 /// When generating the manifest do *not* enforce the rule that the current EventSource class
6014 /// must be the base class for the user-defined type passed in. This allows validation of .net
6015 /// event sources using the new validation code
6017 AllowEventSourceOverride = 0x8,
6021 /// ManifestBuilder is designed to isolate the details of the message of the event from the
6022 /// rest of EventSource. This one happens to create XML.
6024 internal partial class ManifestBuilder
6027 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
6028 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
6030 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
6031 EventManifestOptions flags)
6033 #if FEATURE_MANAGED_ETW_CHANNELS
6034 this.providerName = providerName;
6038 this.resources = resources;
6039 sb = new StringBuilder();
6040 events = new StringBuilder();
6041 templates = new StringBuilder();
6042 opcodeTab = new Dictionary<int, string>();
6043 stringTab = new Dictionary<string, string>();
6044 errors = new List<string>();
6045 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
6047 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6048 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\">");
6049 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6050 sb.Append("<provider name=\"").Append(providerName).
6051 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
6052 if (dllName != null)
6053 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
6055 var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
6056 sb.Append("\" symbol=\"").Append(symbolsName);
6057 sb.Append("\">").AppendLine();
6060 public void AddOpcode(string name, int value)
6062 if ((flags & EventManifestOptions.Strict) != 0)
6064 if (value <= 10 || value >= 239)
6066 ManifestError(Resources.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
6069 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6071 ManifestError(Resources.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
6074 opcodeTab[value] = name;
6076 public void AddTask(string name, int value)
6078 if ((flags & EventManifestOptions.Strict) != 0)
6080 if (value <= 0 || value >= 65535)
6082 ManifestError(Resources.GetResourceString("EventSource_IllegalTaskValue", name, value));
6085 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6087 ManifestError(Resources.GetResourceString("EventSource_TaskCollision", name, prevName, value));
6090 if (taskTab == null)
6091 taskTab = new Dictionary<int, string>();
6092 taskTab[value] = name;
6094 public void AddKeyword(string name, ulong value)
6096 if ((value & (value - 1)) != 0) // Is it a power of 2?
6098 ManifestError(Resources.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
6100 if ((flags & EventManifestOptions.Strict) != 0)
6102 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
6104 ManifestError(Resources.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6107 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6109 ManifestError(Resources.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6112 if (keywordTab == null)
6113 keywordTab = new Dictionary<ulong, string>();
6114 keywordTab[value] = name;
6117 #if FEATURE_MANAGED_ETW_CHANNELS
6119 /// Add a channel. channelAttribute can be null
6121 public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
6123 EventChannel chValue = (EventChannel)value;
6124 if (value < (int)EventChannel.Admin || value > 255)
6125 ManifestError(Resources.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
6126 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
6127 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
6129 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
6130 // but we want to allow them to override the default ones...
6131 ManifestError(Resources.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
6132 name, ((EventChannel)value).ToString()));
6135 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
6137 ulong kwd = GetChannelKeyword(chValue);
6139 if (channelTab == null)
6140 channelTab = new Dictionary<int, ChannelInfo>(4);
6141 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
6144 private EventChannelType EventChannelToChannelType(EventChannel channel)
6146 #if !ES_BUILD_STANDALONE
6147 Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
6149 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
6151 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
6153 EventChannelAttribute attrib = new EventChannelAttribute();
6154 attrib.EventChannelType = EventChannelToChannelType(channel);
6155 if (attrib.EventChannelType <= EventChannelType.Operational)
6156 attrib.Enabled = true;
6160 public ulong[] GetChannelData()
6162 if (this.channelTab == null)
6164 return new ulong[0];
6167 // We create an array indexed by the channel id for fast look up.
6168 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
6170 foreach (var item in this.channelTab.Keys)
6178 ulong[] channelMask = new ulong[maxkey + 1];
6179 foreach (var item in this.channelTab)
6181 channelMask[item.Key] = item.Value.Keywords;
6188 public void StartEvent(string eventName, EventAttribute eventAttribute)
6190 Debug.Assert(numParams == 0);
6191 Debug.Assert(this.eventName == null);
6192 this.eventName = eventName;
6194 byteArrArgIndices = null;
6196 events.Append(" <event").
6197 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
6198 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
6199 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
6200 Append(" symbol=\"").Append(eventName).Append("\"");
6202 // at this point we add to the manifest's stringTab a message that is as-of-yet
6203 // "untranslated to manifest convention", b/c we don't have the number or position
6204 // of any byte[] args (which require string format index updates)
6205 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
6207 if (eventAttribute.Keywords != 0)
6208 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
6209 if (eventAttribute.Opcode != 0)
6210 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
6211 if (eventAttribute.Task != 0)
6212 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
6213 #if FEATURE_MANAGED_ETW_CHANNELS
6214 if (eventAttribute.Channel != 0)
6216 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
6221 public void AddEventParameter(Type type, string name)
6224 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
6225 if (type == typeof(byte[]))
6227 // mark this index as "extraneous" (it has no parallel in the managed signature)
6228 // we use these values in TranslateToManifestConvention()
6229 if (byteArrArgIndices == null)
6230 byteArrArgIndices = new List<int>(4);
6231 byteArrArgIndices.Add(numParams);
6233 // add an extra field to the template representing the length of the binary blob
6235 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
6238 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
6239 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
6240 // as for 'byte[]' args (blob_arg_name + "Size")
6241 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6243 // add "length" attribute to the "blob" field in the template (referencing the field added above)
6244 templates.Append(" length=\"").Append(name).Append("Size\"");
6246 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
6247 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
6249 templates.Append(" map=\"").Append(type.Name).Append("\"");
6250 if (mapsTab == null)
6251 mapsTab = new Dictionary<string, Type>();
6252 if (!mapsTab.ContainsKey(type.Name))
6253 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
6256 templates.Append("/>").AppendLine();
6258 public void EndEvent()
6262 templates.Append(" </template>").AppendLine();
6263 events.Append(" template=\"").Append(eventName).Append("Args\"");
6265 events.Append("/>").AppendLine();
6267 if (byteArrArgIndices != null)
6268 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
6270 // at this point we have all the information we need to translate the C# Message
6271 // to the manifest string we'll put in the stringTab
6273 if (stringTab.TryGetValue("event_" + eventName, out msg))
6275 msg = TranslateToManifestConvention(msg, eventName);
6276 stringTab["event_" + eventName] = msg;
6281 byteArrArgIndices = null;
6284 #if FEATURE_MANAGED_ETW_CHANNELS
6285 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
6286 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
6287 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
6288 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
6289 // to channels by the OS infrastructure).
6290 // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
6291 // assumed that that the keyword for that channel should be that bit.
6292 // otherwise we allocate a channel bit for the channel.
6293 // explicit channel bits are only used by WCF to mimic an existing manifest,
6294 // so we don't dont do error checking.
6295 public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
6297 // strip off any non-channel keywords, since we are only interested in channels here.
6298 channelKeyword &= ValidPredefinedChannelKeywords;
6299 if (channelTab == null)
6301 channelTab = new Dictionary<int, ChannelInfo>(4);
6304 if (channelTab.Count == MaxCountChannels)
6305 ManifestError(Resources.GetResourceString("EventSource_MaxChannelExceeded"));
6308 if (!channelTab.TryGetValue((int)channel, out info))
6310 // If we were not given an explicit channel, allocate one.
6311 if (channelKeyword != 0)
6313 channelKeyword = nextChannelKeywordBit;
6314 nextChannelKeywordBit >>= 1;
6319 channelKeyword = info.Keywords;
6322 return channelKeyword;
6326 public byte[] CreateManifest()
6328 string str = CreateManifestString();
6329 return Encoding.UTF8.GetBytes(str);
6332 public IList<string> Errors { get { return errors; } }
6335 /// When validating an event source it adds the error to the error collection.
6336 /// When not validating it throws an exception if runtimeCritical is "true".
6337 /// Otherwise the error is ignored.
6339 /// <param name="msg"></param>
6340 /// <param name="runtimeCritical"></param>
6341 public void ManifestError(string msg, bool runtimeCritical = false)
6343 if ((flags & EventManifestOptions.Strict) != 0)
6345 else if (runtimeCritical)
6346 throw new ArgumentException(msg);
6349 private string CreateManifestString()
6352 #if FEATURE_MANAGED_ETW_CHANNELS
6353 // Write out the channels
6354 if (channelTab != null)
6356 sb.Append(" <channels>").AppendLine();
6357 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
6358 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
6359 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
6360 foreach (var kvpair in sortedChannels)
6362 int channel = kvpair.Key;
6363 ChannelInfo channelInfo = kvpair.Value;
6365 string channelType = null;
6366 string elementName = "channel";
6367 bool enabled = false;
6368 string fullName = null;
6369 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6370 string isolation = null;
6371 string access = null;
6373 if (channelInfo.Attribs != null)
6375 var attribs = channelInfo.Attribs;
6376 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
6377 channelType = attribs.EventChannelType.ToString();
6378 enabled = attribs.Enabled;
6379 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6380 if (attribs.ImportChannel != null)
6382 fullName = attribs.ImportChannel;
6383 elementName = "importChannel";
6385 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
6386 isolation = attribs.Isolation.ToString();
6387 access = attribs.Access;
6390 if (fullName == null)
6391 fullName = providerName + "/" + channelInfo.Name;
6393 sb.Append(" <").Append(elementName);
6394 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
6395 sb.Append(" name=\"").Append(fullName).Append("\"");
6396 if (elementName == "channel") // not applicable to importChannels.
6398 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
6399 sb.Append(" value=\"").Append(channel).Append("\"");
6400 if (channelType != null)
6401 sb.Append(" type=\"").Append(channelType).Append("\"");
6402 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
6403 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6405 sb.Append(" access=\"").Append(access).Append("\"");
6406 if (isolation != null)
6407 sb.Append(" isolation=\"").Append(isolation).Append("\"");
6410 sb.Append("/>").AppendLine();
6412 sb.Append(" </channels>").AppendLine();
6416 // Write out the tasks
6417 if (taskTab != null)
6420 sb.Append(" <tasks>").AppendLine();
6421 var sortedTasks = new List<int>(taskTab.Keys);
6423 foreach (int task in sortedTasks)
6425 sb.Append(" <task");
6426 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
6427 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
6429 sb.Append(" </tasks>").AppendLine();
6432 // Write out the maps
6433 if (mapsTab != null)
6435 sb.Append(" <maps>").AppendLine();
6436 foreach (Type enumType in mapsTab.Values)
6438 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
6439 string mapKind = isbitmap ? "bitMap" : "valueMap";
6440 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
6442 // write out each enum value
6443 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
6444 foreach (FieldInfo staticField in staticFields)
6446 object constantValObj = staticField.GetRawConstantValue();
6447 if (constantValObj != null)
6450 if (constantValObj is int)
6451 hexValue = ((int)constantValObj);
6452 else if (constantValObj is long)
6453 hexValue = ((long)constantValObj);
6457 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
6458 // TODO: Warn people about the dropping of values.
6459 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
6462 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
6463 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
6464 sb.Append("/>").AppendLine();
6467 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
6469 sb.Append(" </maps>").AppendLine();
6472 // Write out the opcodes
6473 sb.Append(" <opcodes>").AppendLine();
6474 var sortedOpcodes = new List<int>(opcodeTab.Keys);
6475 sortedOpcodes.Sort();
6476 foreach (int opcode in sortedOpcodes)
6478 sb.Append(" <opcode");
6479 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
6480 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
6482 sb.Append(" </opcodes>").AppendLine();
6484 // Write out the keywords
6485 if (keywordTab != null)
6487 sb.Append(" <keywords>").AppendLine();
6488 var sortedKeywords = new List<ulong>(keywordTab.Keys);
6489 sortedKeywords.Sort();
6490 foreach (ulong keyword in sortedKeywords)
6492 sb.Append(" <keyword");
6493 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
6494 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
6496 sb.Append(" </keywords>").AppendLine();
6499 sb.Append(" <events>").AppendLine();
6501 sb.Append(" </events>").AppendLine();
6503 sb.Append(" <templates>").AppendLine();
6504 if (templates.Length > 0)
6506 sb.Append(templates);
6510 // Work around a cornercase ETW issue where a manifest with no templates causes
6511 // ETW events to not get sent to their associated channel.
6512 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
6514 sb.Append(" </templates>").AppendLine();
6516 sb.Append("</provider>").AppendLine();
6517 sb.Append("</events>").AppendLine();
6518 sb.Append("</instrumentation>").AppendLine();
6520 // Output the localization information.
6521 sb.Append("<localization>").AppendLine();
6523 List<CultureInfo> cultures = null;
6524 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
6526 cultures = GetSupportedCultures(resources);
6530 cultures = new List<CultureInfo>();
6531 cultures.Add(CultureInfo.CurrentUICulture);
6533 #if ES_BUILD_STANDALONE || ES_BUILD_PN
6534 var sortedStrings = new List<string>(stringTab.Keys);
6535 sortedStrings.Sort();
6538 var sortedStrings = new string[stringTab.Keys.Count];
6539 stringTab.Keys.CopyTo(sortedStrings, 0);
6540 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
6541 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
6542 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
6543 // access BinaryCompatibility.
6544 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, string.Compare);
6546 foreach (var ci in cultures)
6548 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
6549 sb.Append(" <stringTable>").AppendLine();
6551 foreach (var stringKey in sortedStrings)
6553 string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
6554 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
6556 sb.Append(" </stringTable>").AppendLine();
6557 sb.Append(" </resources>").AppendLine();
6559 sb.Append("</localization>").AppendLine();
6560 sb.AppendLine("</instrumentationManifest>");
6561 return sb.ToString();
6565 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
6567 stringBuilder.Append(" name=\"").Append(name).Append("\"");
6568 WriteMessageAttrib(sb, elementName, name, name);
6570 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
6572 string key = elementName + "_" + name;
6573 // See if the user wants things localized.
6574 if (resources != null)
6576 // resource fallback: strings in the neutral culture will take precedence over inline strings
6577 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
6578 if (localizedString != null)
6579 value = localizedString;
6584 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
6586 if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
6588 ManifestError(Resources.GetResourceString("EventSource_DuplicateStringKey", key), true);
6592 stringTab[key] = value;
6594 internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
6596 string value = null;
6597 if (resources != null)
6599 string localizedString = resources.GetString(key, ci);
6600 if (localizedString != null)
6602 value = localizedString;
6603 if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
6605 var evtName = key.Substring("event_".Length);
6606 value = TranslateToManifestConvention(value, evtName);
6610 if (etwFormat && value == null)
6611 stringTab.TryGetValue(key, out value);
6617 /// There's no API to enumerate all languages an assembly is localized into, so instead
6618 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
6621 /// <param name="resources"></param>
6622 /// <returns></returns>
6623 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
6625 var cultures = new List<CultureInfo>();
6627 if (!cultures.Contains(CultureInfo.CurrentUICulture))
6628 cultures.Insert(0, CultureInfo.CurrentUICulture);
6632 private static string GetLevelName(EventLevel level)
6634 return (((int)level >= 16) ? "" : "win:") + level.ToString();
6637 #if FEATURE_MANAGED_ETW_CHANNELS
6638 private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
6640 ChannelInfo info = null;
6641 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6643 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6644 ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6646 // allow channels to be auto-defined. The well known ones get their well known names, and the
6647 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
6648 if (channelTab == null)
6649 channelTab = new Dictionary<int, ChannelInfo>(4);
6651 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
6652 if (EventChannel.Debug < channel)
6653 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
6655 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6656 if (!channelTab.TryGetValue((int)channel, out info))
6657 ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6659 // events that specify admin channels *must* have non-null "Message" attributes
6660 if (resources != null && eventMessage == null)
6661 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6662 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6663 ManifestError(Resources.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
6667 private string GetTaskName(EventTask task, string eventName)
6669 if (task == EventTask.None)
6673 if (taskTab == null)
6674 taskTab = new Dictionary<int, string>();
6675 if (!taskTab.TryGetValue((int)task, out ret))
6676 ret = taskTab[(int)task] = eventName;
6680 private string GetOpcodeName(EventOpcode opcode, string eventName)
6684 case EventOpcode.Info:
6686 case EventOpcode.Start:
6688 case EventOpcode.Stop:
6690 case EventOpcode.DataCollectionStart:
6691 return "win:DC_Start";
6692 case EventOpcode.DataCollectionStop:
6693 return "win:DC_Stop";
6694 case EventOpcode.Extension:
6695 return "win:Extension";
6696 case EventOpcode.Reply:
6698 case EventOpcode.Resume:
6699 return "win:Resume";
6700 case EventOpcode.Suspend:
6701 return "win:Suspend";
6702 case EventOpcode.Send:
6704 case EventOpcode.Receive:
6705 return "win:Receive";
6709 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6711 ManifestError(Resources.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
6717 private string GetKeywords(ulong keywords, string eventName)
6719 #if FEATURE_MANAGED_ETW_CHANNELS
6720 // ignore keywords associate with channels
6721 // See ValidPredefinedChannelKeywords def for more.
6722 keywords &= ~ValidPredefinedChannelKeywords;
6726 for (ulong bit = 1; bit != 0; bit <<= 1)
6728 if ((keywords & bit) != 0)
6730 string keyword = null;
6731 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6732 (bit >= (ulong)0x1000000000000))
6734 // do not report Windows reserved keywords in the manifest (this allows the code
6735 // to be resilient to potential renaming of these keywords)
6736 keyword = string.Empty;
6738 if (keyword == null)
6740 ManifestError(Resources.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6741 keyword = string.Empty;
6743 if (ret.Length != 0 && keyword.Length != 0)
6745 ret = ret + keyword;
6751 private string GetTypeName(Type type)
6755 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6756 var typeName = GetTypeName(fields[0].FieldType);
6757 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6760 return GetTypeNameHelper(type);
6763 private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
6765 if (stringBuilder == null)
6766 stringBuilder = new StringBuilder();
6767 stringBuilder.Append(eventMessage, startIndex, count);
6770 private static readonly string[] s_escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" };
6771 // Manifest messages use %N conventions for their message substitutions. Translate from
6772 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6773 private string TranslateToManifestConvention(string eventMessage, string evtName)
6775 StringBuilder stringBuilder = null; // We lazily create this
6776 int writtenSoFar = 0;
6780 if (i >= eventMessage.Length)
6782 if (stringBuilder == null)
6783 return eventMessage;
6784 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6785 return stringBuilder.ToString();
6788 if (eventMessage[i] == '%')
6790 // handle format message escaping character '%' by escaping it
6791 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6792 stringBuilder.Append("%%");
6796 else if (i < eventMessage.Length - 1 &&
6797 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6799 // handle C# escaped '{" and '}'
6800 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6801 stringBuilder.Append(eventMessage[i]);
6805 else if (eventMessage[i] == '{')
6807 int leftBracket = i;
6810 while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
6812 argNum = argNum * 10 + eventMessage[i] - '0';
6815 if (i < eventMessage.Length && eventMessage[i] == '}')
6818 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6819 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6820 stringBuilder.Append('%').Append(manIndex);
6821 // An '!' after the insert specifier {n} will be interpreted as a literal.
6822 // We'll escape it so that mc.exe does not attempt to consider it the
6823 // beginning of a format string.
6824 if (i < eventMessage.Length && eventMessage[i] == '!')
6827 stringBuilder.Append("%!");
6833 ManifestError(Resources.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
6836 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6838 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6840 stringBuilder.Append(s_escapes[chIdx]);
6848 private int TranslateIndexToManifestConvention(int idx, string evtName)
6850 List<int> byteArrArgIndices;
6851 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6853 foreach (var byArrIdx in byteArrArgIndices)
6855 if (idx >= byArrIdx)
6864 #if FEATURE_MANAGED_ETW_CHANNELS
6868 public ulong Keywords;
6869 public EventChannelAttribute Attribs;
6873 Dictionary<int, string> opcodeTab;
6874 Dictionary<int, string> taskTab;
6875 #if FEATURE_MANAGED_ETW_CHANNELS
6876 Dictionary<int, ChannelInfo> channelTab;
6878 Dictionary<ulong, string> keywordTab;
6879 Dictionary<string, Type> mapsTab;
6881 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6883 #if FEATURE_MANAGED_ETW_CHANNELS
6884 // WCF used EventSource to mimic a existing ETW manifest. To support this
6885 // in just their case, we allowed them to specify the keywords associated
6886 // with their channels explicitly. ValidPredefinedChannelKeywords is
6887 // this set of channel keywords that we allow to be explicitly set. You
6888 // can ignore these bits otherwise.
6889 internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
6890 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
6891 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6894 StringBuilder sb; // Holds the provider information.
6895 StringBuilder events; // Holds the events.
6896 StringBuilder templates;
6898 #if FEATURE_MANAGED_ETW_CHANNELS
6899 string providerName;
6901 ResourceManager resources; // Look up localized strings here.
6902 EventManifestOptions flags;
6903 IList<string> errors; // list of currently encountered errors
6904 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6906 // State we track between StartEvent and EndEvent.
6907 string eventName; // Name of the event currently being processed.
6908 int numParams; // keeps track of the number of args the event has.
6909 List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
6914 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6916 internal struct ManifestEnvelope
6918 public const int MaxChunkSize = 0xFF00;
6919 public enum ManifestFormats : byte
6921 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6924 #if FEATURE_MANAGED_ETW
6925 public ManifestFormats Format;
6926 public byte MajorVersion;
6927 public byte MinorVersion;
6929 public ushort TotalChunks;
6930 public ushort ChunkNumber;