1 // Copyright (c) Microsoft Corporation. All rights reserved
2 // Copyright (c) Microsoft Corporation. All rights reserved
3 // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
4 // It is available from http://www.codeplex.com/hyperAddin
5 #define FEATURE_MANAGED_ETW
7 #if !ES_BUILD_STANDALONE
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.
172 #if FEATURE_ACTIVITYSAMPLING
173 using System.Collections.Concurrent;
175 using System.Collections.Generic;
176 using System.Collections.ObjectModel;
177 using System.Diagnostics;
178 using System.Diagnostics.CodeAnalysis;
179 using System.Globalization;
180 using System.Reflection;
181 using System.Resources;
182 using System.Security;
183 using System.Security.Permissions;
185 using System.Threading;
186 using Microsoft.Win32;
188 #if ES_BUILD_STANDALONE
189 using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
190 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
192 using EventDescriptor = System.Diagnostics.Tracing.EventDescriptor;
195 using Microsoft.Reflection;
197 #if !ES_BUILD_AGAINST_DOTNET_V35
198 using Contract = System.Diagnostics.Contracts.Contract;
200 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
203 #if ES_BUILD_STANDALONE
204 namespace Microsoft.Diagnostics.Tracing
206 namespace System.Diagnostics.Tracing
210 /// This class is meant to be inherited by a user-defined event source in order to define a managed
211 /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
212 /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
213 /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
214 /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
215 /// is sufficient for many users.
217 /// To achieve more control over the ETW provider manifest exposed by the event source type, the
218 /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
220 /// For very advanced EventSources, it is possible to intercept the commands being given to the
221 /// eventSource and change what filtering is done (see EventListener.EnableEvents and
222 /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
223 /// e.g. dumping a data structure (see EventSource.SendCommand and
224 /// <see cref="EventSource.OnEventCommand"/>).
226 /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
227 /// It is also possible to control and intercept the data dispatcher programmatically. See
228 /// <see cref="EventListener"/> for more.
232 /// This is a minimal definition for a custom event source:
234 /// [EventSource(Name="Samples-Demos-Minimal")]
235 /// sealed class MinimalEventSource : EventSource
237 /// public static MinimalEventSource Log = new MinimalEventSource();
238 /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
239 /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
240 /// private MinimalEventSource() {}
244 public partial class EventSource : IDisposable
247 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
249 public string Name { get { return m_name; } }
251 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
253 public Guid Guid { get { return m_guid; } }
256 /// Returns true if the eventSource has been enabled at all. This is the prefered test
257 /// to be performed before a relatively expensive EventSource operation.
259 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
260 public bool IsEnabled()
262 return m_eventSourceEnabled;
266 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
268 /// Note that the result of this function is only an approximation on whether a particular
269 /// event is active or not. It is only meant to be used as way of avoiding expensive
270 /// computation for logging when logging is not on, therefore it sometimes returns false
271 /// positives (but is always accurate when returning false). EventSources are free to
272 /// have additional filtering.
274 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
275 public bool IsEnabled(EventLevel level, EventKeywords keywords)
277 return IsEnabled(level, keywords, EventChannel.None);
281 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
282 /// if 'keywords' specifies a channel bit for a channel that is enabled.
284 /// Note that the result of this function only an approximation on whether a particular
285 /// event is active or not. It is only meant to be used as way of avoiding expensive
286 /// computation for logging when logging is not on, therefore it sometimes returns false
287 /// positives (but is always accurate when returning false). EventSources are free to
288 /// have additional filtering.
290 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
291 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
293 if (!m_eventSourceEnabled)
296 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
299 #if !FEATURE_ACTIVITYSAMPLING
303 #else // FEATURE_ACTIVITYSAMPLING
307 #if OPTIMIZE_IS_ENABLED
308 //================================================================================
309 // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
310 // in case activity tracing/sampling is enabled. The added complexity of this
311 // code however weighs against having it "on" until we know it's really needed.
312 // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
313 //================================================================================
315 // At this point we believe the event is enabled, however we now need to check
316 // if we filter because of activity
318 // Optimization, all activity filters also register a delegate here, so if there
319 // is no delegate, we know there are no activity filters, which means that there
320 // is no additional filtering, which means that we can return true immediately.
321 if (s_activityDying == null)
324 // if there's at least one legacy ETW listener we can't filter this
325 if (m_legacySessions != null && m_legacySessions.Count > 0)
328 // if any event ID that triggers a new activity, or "transfers" activities
329 // is covered by 'keywords' we can't filter this
330 if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
333 // See if all listeners have activity filters that would block the event.
334 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
336 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
337 if (etwSession == null)
340 ActivityFilter activityFilter = etwSession.m_activityFilter;
341 if (activityFilter == null ||
342 ActivityFilter.GetFilter(activityFilter, this) == null)
344 // No activity filter for ETW, if event is active for ETW, we can't filter.
345 for (int i = 0; i < m_eventData.Length; i++)
346 if (m_eventData[i].EnabledForETW)
349 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
353 // for regular event listeners
354 var curDispatcher = m_Dispatchers;
355 while (curDispatcher != null)
357 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
358 if (activityFilter == null)
360 // See if any event is enabled.
361 for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
362 if (curDispatcher.m_EventEnabled[i])
365 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
367 curDispatcher = curDispatcher.m_Next;
370 // Every listener has an activity filter that is blocking writing the event,
371 // thus the event is not enabled.
373 #endif // OPTIMIZE_IS_ENABLED
375 #endif // FEATURE_ACTIVITYSAMPLING
379 /// Returns the settings for the event source instance
381 public EventSourceSettings Settings
383 get { return m_config; }
388 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
389 /// This API allows you to compute this without actually creating an instance of the EventSource.
390 /// It only needs to reflect over the type.
392 public static Guid GetGuid(Type eventSourceType)
394 if (eventSourceType == null)
395 throw new ArgumentNullException("eventSourceType");
396 Contract.EndContractBlock();
398 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
399 string name = eventSourceType.Name;
402 if (attrib.Guid != null)
405 #if !ES_BUILD_AGAINST_DOTNET_V35
406 if (Guid.TryParse(attrib.Guid, out g))
409 try { return new Guid(attrib.Guid); }
410 catch (Exception) { }
414 if (attrib.Name != null)
419 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidTypeName"), "eventSourceType");
421 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
424 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
425 /// This API allows you to compute this without actually creating an instance of the EventSource.
426 /// It only needs to reflect over the type.
428 public static string GetName(Type eventSourceType)
430 return GetName(eventSourceType, EventManifestOptions.None);
434 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
435 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
436 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
437 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
439 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
440 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
441 /// which it is embedded. This parameter specifies what name will be used</param>
442 /// <returns>The XML data string</returns>
443 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
445 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
448 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
449 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
450 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
451 /// ensures that the entries in the event log will be "optimally" localized.
453 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
454 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
455 /// which it is embedded. This parameter specifies what name will be used</param>
456 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
457 /// this returns null when the eventSourceType does not require explicit registration</param>
458 /// <returns>The XML data string or null</returns>
459 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
461 if (eventSourceType == null)
462 throw new ArgumentNullException("eventSourceType");
463 Contract.EndContractBlock();
465 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
466 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
469 // EventListener support
471 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
473 /// <returns></returns>
474 public static IEnumerable<EventSource> GetSources()
476 var ret = new List<EventSource>();
477 lock (EventListener.EventListenersLock)
479 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
481 EventSource eventSource = eventSourceRef.Target as EventSource;
482 if (eventSource != null && !eventSource.IsDisposed)
483 ret.Add(eventSource);
490 /// Send a command to a particular EventSource identified by 'eventSource'.
491 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
492 /// callback. What the EventSource does with the command and its arguments are from
493 /// that point EventSource-specific.
495 /// <param name="eventSource">The instance of EventSource to send the command to</param>
496 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
497 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
498 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
500 if (eventSource == null)
501 throw new ArgumentNullException("eventSource");
503 // User-defined EventCommands should not conflict with the reserved commands.
504 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
505 throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidCommand"), "command");
507 eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
510 // ActivityID support (see also WriteEventWithRelatedActivityIdCore)
512 /// When a thread starts work that is on behalf of 'something else' (typically another
513 /// thread or network request) it should mark the thread as working on that other work.
514 /// This API marks the current thread as working on activity 'activityID'. This API
515 /// should be used when the caller knows the thread's current activity (the one being
516 /// overwritten) has completed. Otherwise, callers should prefer the overload that
517 /// return the oldActivityThatWillContinue (below).
519 /// All events created with the EventSource on this thread are also tagged with the
520 /// activity ID of the thread.
522 /// It is common, and good practice after setting the thread to an activity to log an event
523 /// with a 'start' opcode to indicate that precise time/thread where the new activity
526 /// <param name="activityId">A Guid that represents the new activity with which to mark
527 /// the current thread</param>
528 [System.Security.SecuritySafeCritical]
529 public static void SetCurrentThreadActivityId(Guid activityId)
531 #if FEATURE_ACTIVITYSAMPLING
532 Guid newId = activityId;
533 #endif // FEATURE_ACTIVITYSAMPLING
534 // We ignore errors to keep with the convention that EventSources do not throw errors.
535 // Note we can't access m_throwOnWrites because this is a static method.
536 if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
537 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
538 ref activityId) == 0)
540 #if FEATURE_ACTIVITYSAMPLING
541 var activityDying = s_activityDying;
542 if (activityDying != null && newId != activityId)
544 if (activityId == Guid.Empty)
546 activityId = FallbackActivityId;
548 // OutputDebugString(string.Format("Activity dying: {0} -> {1}", activityId, newId));
549 activityDying(activityId); // This is actually the OLD activity ID.
551 #endif // FEATURE_ACTIVITYSAMPLING
553 if (System.Threading.Tasks.TplEtwProvider.Log != null)
554 System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId);
558 /// When a thread starts work that is on behalf of 'something else' (typically another
559 /// thread or network request) it should mark the thread as working on that other work.
560 /// This API marks the current thread as working on activity 'activityID'. It returns
561 /// whatever activity the thread was previously marked with. There is a convention that
562 /// callers can assume that callees restore this activity mark before the callee returns.
563 /// To encourage this this API returns the old activity, so that it can be restored later.
565 /// All events created with the EventSource on this thread are also tagged with the
566 /// activity ID of the thread.
568 /// It is common, and good practice after setting the thread to an activity to log an event
569 /// with a 'start' opcode to indicate that precise time/thread where the new activity
572 /// <param name="activityId">A Guid that represents the new activity with which to mark
573 /// the current thread</param>
574 /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
575 /// which will continue at some point in the future, on the current thread</param>
576 [System.Security.SecuritySafeCritical]
577 public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
579 oldActivityThatWillContinue = activityId;
580 // We ignore errors to keep with the convention that EventSources do not throw errors.
581 // Note we can't access m_throwOnWrites because this is a static method.
582 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
583 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
584 ref oldActivityThatWillContinue);
586 // We don't call the activityDying callback here because the caller has declared that
588 if (System.Threading.Tasks.TplEtwProvider.Log != null)
589 System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId);
593 /// Retrieves the ETW activity ID associated with the current thread.
595 public static Guid CurrentThreadActivityId
597 [System.Security.SecurityCritical]
600 // We ignore errors to keep with the convention that EventSources do not throw
601 // errors. Note we can't access m_throwOnWrites because this is a static method.
602 Guid retVal = new Guid();
603 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
604 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
610 #if !ES_BUILD_STANDALONE
612 /// This property allows EventSource code to appropriately handle as "different"
613 /// activities started on different threads that have not had an activity created on them.
615 internal static Guid InternalCurrentThreadActivityId
617 [System.Security.SecurityCritical]
620 Guid retval = CurrentThreadActivityId;
621 if (retval == Guid.Empty)
623 retval = FallbackActivityId;
629 internal static Guid FallbackActivityId
631 [System.Security.SecurityCritical]
634 #pragma warning disable 612, 618
635 // Managed thread IDs are more aggressively re-used than native thread IDs,
636 // so we'll use the latter...
637 return new Guid(unchecked((uint)AppDomain.GetCurrentThreadId()),
638 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
639 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
640 #pragma warning restore 612, 618
643 #endif // !ES_BUILD_STANDALONE
645 // Error APIs. (We don't throw by default, but you can probe for status)
649 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
650 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
652 /// The event source constructor does not throw exceptions. Instead we remember any exception that
653 /// was generated (it is also logged to Trace.WriteLine).
655 public Exception ConstructionException { get { return m_constructionException; } }
658 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
659 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
660 /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
661 /// construction time and can be retrieved by using this GetTrait API.
663 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
664 /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
665 public string GetTrait(string key)
667 if (m_traits != null)
669 for (int i = 0; i < m_traits.Length - 1; i += 2)
671 if (m_traits[i] == key)
672 return m_traits[i + 1];
679 /// Displays the name and GUID for the eventSource for debugging purposes.
681 public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); }
685 /// This is the constructor that most users will use to create their eventSource. It takes
686 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
687 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
688 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
689 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
690 /// the ETW provider name.
692 protected EventSource()
693 : this(EventSourceSettings.EtwManifestEventFormat)
698 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
699 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
700 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
701 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
702 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
703 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
705 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
707 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
708 protected EventSource(bool throwOnEventWriteErrors)
709 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
713 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
715 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
718 /// Construct an EventSource with additional non-default settings.
720 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
721 /// The first string is the key and the second is the value. These are not interpreted by EventSource
722 /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
724 /// <param name="settings">See EventSourceSettings for more.</param>
725 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
726 protected EventSource(EventSourceSettings settings, params string[] traits)
728 m_config = ValidateSettings(settings);
729 var myType = this.GetType();
730 Initialize(GetGuid(myType), GetName(myType), traits);
734 /// This method is called when the eventSource is updated by the controller.
736 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
738 #pragma warning disable 1591
739 // optimized for common signatures (no args)
740 [SecuritySafeCritical]
741 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
742 protected unsafe void WriteEvent(int eventId)
744 WriteEventCore(eventId, 0, null);
747 // optimized for common signatures (ints)
748 [SecuritySafeCritical]
749 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
750 protected unsafe void WriteEvent(int eventId, int arg1)
752 if (m_eventSourceEnabled)
754 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
755 descrs[0].DataPointer = (IntPtr)(&arg1);
757 WriteEventCore(eventId, 1, descrs);
761 [SecuritySafeCritical]
762 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
763 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
765 if (m_eventSourceEnabled)
767 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
768 descrs[0].DataPointer = (IntPtr)(&arg1);
770 descrs[1].DataPointer = (IntPtr)(&arg2);
772 WriteEventCore(eventId, 2, descrs);
776 [SecuritySafeCritical]
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, int arg1, int arg2, int arg3)
780 if (m_eventSourceEnabled)
782 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
783 descrs[0].DataPointer = (IntPtr)(&arg1);
785 descrs[1].DataPointer = (IntPtr)(&arg2);
787 descrs[2].DataPointer = (IntPtr)(&arg3);
789 WriteEventCore(eventId, 3, descrs);
793 // optimized for common signatures (longs)
794 [SecuritySafeCritical]
795 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
796 protected unsafe void WriteEvent(int eventId, long arg1)
798 if (m_eventSourceEnabled)
800 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
801 descrs[0].DataPointer = (IntPtr)(&arg1);
803 WriteEventCore(eventId, 1, descrs);
807 [SecuritySafeCritical]
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, long arg1, long arg2)
811 if (m_eventSourceEnabled)
813 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
814 descrs[0].DataPointer = (IntPtr)(&arg1);
816 descrs[1].DataPointer = (IntPtr)(&arg2);
818 WriteEventCore(eventId, 2, descrs);
822 [SecuritySafeCritical]
823 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
824 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
826 if (m_eventSourceEnabled)
828 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
829 descrs[0].DataPointer = (IntPtr)(&arg1);
831 descrs[1].DataPointer = (IntPtr)(&arg2);
833 descrs[2].DataPointer = (IntPtr)(&arg3);
835 WriteEventCore(eventId, 3, descrs);
839 // optimized for common signatures (strings)
840 [SecuritySafeCritical]
841 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
842 protected unsafe void WriteEvent(int eventId, string arg1)
844 if (m_eventSourceEnabled)
846 if (arg1 == null) arg1 = "";
847 fixed (char* string1Bytes = arg1)
849 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
850 descrs[0].DataPointer = (IntPtr)string1Bytes;
851 descrs[0].Size = ((arg1.Length + 1) * 2);
852 WriteEventCore(eventId, 1, descrs);
857 [SecuritySafeCritical]
858 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
859 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
861 if (m_eventSourceEnabled)
863 if (arg1 == null) arg1 = "";
864 if (arg2 == null) arg2 = "";
865 fixed (char* string1Bytes = arg1)
866 fixed (char* string2Bytes = arg2)
868 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
869 descrs[0].DataPointer = (IntPtr)string1Bytes;
870 descrs[0].Size = ((arg1.Length + 1) * 2);
871 descrs[1].DataPointer = (IntPtr)string2Bytes;
872 descrs[1].Size = ((arg2.Length + 1) * 2);
873 WriteEventCore(eventId, 2, descrs);
878 [SecuritySafeCritical]
879 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
880 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
882 if (m_eventSourceEnabled)
884 if (arg1 == null) arg1 = "";
885 if (arg2 == null) arg2 = "";
886 if (arg3 == null) arg3 = "";
887 fixed (char* string1Bytes = arg1)
888 fixed (char* string2Bytes = arg2)
889 fixed (char* string3Bytes = arg3)
891 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
892 descrs[0].DataPointer = (IntPtr)string1Bytes;
893 descrs[0].Size = ((arg1.Length + 1) * 2);
894 descrs[1].DataPointer = (IntPtr)string2Bytes;
895 descrs[1].Size = ((arg2.Length + 1) * 2);
896 descrs[2].DataPointer = (IntPtr)string3Bytes;
897 descrs[2].Size = ((arg3.Length + 1) * 2);
898 WriteEventCore(eventId, 3, descrs);
903 // optimized for common signatures (string and ints)
904 [SecuritySafeCritical]
905 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
906 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
908 if (m_eventSourceEnabled)
910 if (arg1 == null) arg1 = "";
911 fixed (char* string1Bytes = arg1)
913 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
914 descrs[0].DataPointer = (IntPtr)string1Bytes;
915 descrs[0].Size = ((arg1.Length + 1) * 2);
916 descrs[1].DataPointer = (IntPtr)(&arg2);
918 WriteEventCore(eventId, 2, descrs);
923 [SecuritySafeCritical]
924 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
925 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
927 if (m_eventSourceEnabled)
929 if (arg1 == null) arg1 = "";
930 fixed (char* string1Bytes = arg1)
932 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
933 descrs[0].DataPointer = (IntPtr)string1Bytes;
934 descrs[0].Size = ((arg1.Length + 1) * 2);
935 descrs[1].DataPointer = (IntPtr)(&arg2);
937 descrs[2].DataPointer = (IntPtr)(&arg3);
939 WriteEventCore(eventId, 3, descrs);
944 // optimized for common signatures (string and longs)
945 [SecuritySafeCritical]
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, string arg1, long arg2)
949 if (m_eventSourceEnabled)
951 if (arg1 == null) arg1 = "";
952 fixed (char* string1Bytes = arg1)
954 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
955 descrs[0].DataPointer = (IntPtr)string1Bytes;
956 descrs[0].Size = ((arg1.Length + 1) * 2);
957 descrs[1].DataPointer = (IntPtr)(&arg2);
959 WriteEventCore(eventId, 2, descrs);
964 // optimized for common signatures (long and string)
965 [SecuritySafeCritical]
966 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
967 protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
969 if (m_eventSourceEnabled)
971 if (arg2 == null) arg2 = "";
972 fixed (char* string2Bytes = arg2)
974 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
975 descrs[0].DataPointer = (IntPtr)(&arg1);
977 descrs[1].DataPointer = (IntPtr)string2Bytes;
978 descrs[1].Size = ((arg2.Length + 1) * 2);
979 WriteEventCore(eventId, 2, descrs);
984 // optimized for common signatures (int and string)
985 [SecuritySafeCritical]
986 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
987 protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
989 if (m_eventSourceEnabled)
991 if (arg2 == null) arg2 = "";
992 fixed (char* string2Bytes = arg2)
994 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
995 descrs[0].DataPointer = (IntPtr)(&arg1);
997 descrs[1].DataPointer = (IntPtr)string2Bytes;
998 descrs[1].Size = ((arg2.Length + 1) * 2);
999 WriteEventCore(eventId, 2, descrs);
1004 [SecuritySafeCritical]
1005 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1006 protected unsafe void WriteEvent(int eventId, byte[] arg1)
1008 if (m_eventSourceEnabled)
1010 if (arg1 == null) arg1 = new byte[0];
1011 int blobSize = arg1.Length;
1012 fixed (byte* blob = &arg1[0])
1014 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1015 descrs[0].DataPointer = (IntPtr)(&blobSize);
1017 descrs[1].DataPointer = (IntPtr)blob;
1018 descrs[1].Size = blobSize;
1019 WriteEventCore(eventId, 2, descrs);
1024 [SecuritySafeCritical]
1025 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1026 protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
1028 if (m_eventSourceEnabled)
1030 if (arg2 == null) arg2 = new byte[0];
1031 int blobSize = arg2.Length;
1032 fixed (byte* blob = &arg2[0])
1034 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1035 descrs[0].DataPointer = (IntPtr)(&arg1);
1037 descrs[1].DataPointer = (IntPtr)(&blobSize);
1039 descrs[2].DataPointer = (IntPtr)blob;
1040 descrs[2].Size = blobSize;
1041 WriteEventCore(eventId, 3, descrs);
1046 #pragma warning restore 1591
1049 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1051 protected internal struct EventData
1054 /// Address where the one argument lives (if this points to managed memory you must ensure the
1055 /// managed object is pinned.
1057 public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
1059 /// Size of the argument referenced by DataPointer
1061 public int Size { get { return m_Size; } set { m_Size = value; } }
1065 /// Initializes the members of this EventData object to point at a previously-pinned
1066 /// tracelogging-compatible metadata blob.
1068 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1069 /// <param name="size">The size of the metadata blob.</param>
1070 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1072 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1074 this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
1076 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1079 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1080 // the way EventWrite wants it.
1081 internal long m_Ptr;
1082 internal int m_Size;
1083 #pragma warning disable 0649
1084 internal int m_Reserved; // Used to pad the size to match the Win32 API
1085 #pragma warning restore 0649
1090 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1091 /// do this, while straightforward, is unsafe.
1095 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1097 /// if (IsEnabled())
1099 /// if (arg2 == null) arg2 = "";
1100 /// fixed (char* string2Bytes = arg2)
1102 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1103 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1104 /// descrs[0].Size = 8;
1105 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1106 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1107 /// WriteEventCore(eventId, 2, descrs);
1114 [CLSCompliant(false)]
1115 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1117 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1121 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1122 /// that you use to do this, while straightforward, is unsafe. The only difference from
1123 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1127 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1129 /// if (IsEnabled())
1131 /// if (arg2 == null) arg2 = "";
1132 /// fixed (char* string2Bytes = arg2)
1134 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1135 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1136 /// descrs[0].Size = 8;
1137 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1138 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1139 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1146 [CLSCompliant(false)]
1147 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1149 if (m_eventSourceEnabled)
1153 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1154 if (relatedActivityId != null)
1155 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
1157 #if FEATURE_MANAGED_ETW
1158 if (m_eventData[eventId].EnabledForETW)
1160 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1161 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1162 Guid* pActivityId = null;
1163 Guid activityId = Guid.Empty;
1164 Guid relActivityId = Guid.Empty;
1166 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1167 ((activityOptions & EventActivityOptions.Disable) == 0))
1169 if (opcode == EventOpcode.Start)
1171 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1173 else if (opcode == EventOpcode.Stop)
1175 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1178 if (activityId != Guid.Empty)
1179 pActivityId = &activityId;
1180 if (relActivityId != Guid.Empty)
1181 relatedActivityId = &relActivityId;
1184 #if FEATURE_ACTIVITYSAMPLING
1185 // this code should be kept in [....] with WriteEventVarargs().
1186 SessionMask etwSessions = SessionMask.All;
1187 // only compute etwSessions if there are *any* ETW filters enabled...
1188 if ((ulong)m_curLiveSessions != 0)
1189 etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
1190 // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
1191 // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
1193 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1195 if (!SelfDescribingEvents)
1197 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1199 // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
1200 // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
1201 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1202 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1203 // synthesize a new one
1204 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1205 ThrowEventSourceException();
1209 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1210 // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
1211 // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
1212 // only some of the ETW sessions will receive this event. Synthesize a new
1213 // Descriptor whose Keywords field will have the appropriate bits set.
1214 // etwSessions might be 0, if there are legacy ETW listeners that want this event
1215 var desc = new EventDescriptor(
1216 m_eventData[eventId].Descriptor.EventId,
1217 m_eventData[eventId].Descriptor.Version,
1218 m_eventData[eventId].Descriptor.Channel,
1219 m_eventData[eventId].Descriptor.Level,
1220 m_eventData[eventId].Descriptor.Opcode,
1221 m_eventData[eventId].Descriptor.Task,
1222 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1224 if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1225 ThrowEventSourceException();
1230 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1233 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1235 m_eventData[eventId].Parameters);
1236 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1239 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1241 EventSourceOptions opt = new EventSourceOptions
1243 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1244 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1245 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1248 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1252 if (!SelfDescribingEvents)
1254 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1255 ThrowEventSourceException();
1259 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1262 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1263 m_eventData[eventId].Tags,
1264 m_eventData[eventId].Parameters);
1265 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1268 EventSourceOptions opt = new EventSourceOptions
1270 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1271 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1272 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1275 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1277 #endif // FEATURE_ACTIVITYSAMPLING
1279 #endif // FEATURE_MANAGED_ETW
1281 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1282 WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
1284 catch (Exception ex)
1286 if (ex is EventSourceException)
1289 ThrowEventSourceException(ex);
1294 // fallback varags helpers.
1296 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1297 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1298 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1299 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1300 /// check so that the varargs call is not made when the EventSource is not active.
1302 [SecuritySafeCritical]
1303 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1304 protected unsafe void WriteEvent(int eventId, params object[] args)
1306 WriteEventVarargs(eventId, null, args);
1310 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1311 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1312 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1313 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1314 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1315 /// check so that the varargs call is not made when the EventSource is not active.
1317 [SecuritySafeCritical]
1318 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1320 WriteEventVarargs(eventId, &relatedActivityId, args);
1325 #region IDisposable Members
1327 /// Disposes of an EventSource.
1329 public void Dispose()
1332 GC.SuppressFinalize(this);
1335 /// Disposes of an EventSource.
1338 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1340 /// 1. We may be called more than once: do nothing after the first call.
1341 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1343 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1344 protected virtual void Dispose(bool disposing)
1348 #if FEATURE_MANAGED_ETW
1349 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1350 // even in scenarios with a high volume of ETW events.
1351 if (m_eventSourceEnabled)
1355 SendManifest(m_rawManifest);
1358 { } // If it fails, simply give up.
1359 m_eventSourceEnabled = false;
1361 if (m_provider != null)
1363 m_provider.Dispose();
1368 m_eventSourceEnabled = false;
1371 /// Finalizer for EventSource
1375 this.Dispose(false);
1380 #if FEATURE_ACTIVITYSAMPLING
1381 internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
1383 Contract.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
1385 if (m_eventSourceEnabled)
1387 if (listener == null)
1389 WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
1393 List<object> arg = new List<object>();
1395 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1396 eventCallbackArgs.EventId = 0;
1397 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(arg);
1398 listener.OnEventWritten(eventCallbackArgs);
1404 private unsafe void WriteEventRaw(
1405 ref EventDescriptor eventDescriptor,
1407 Guid* relatedActivityID,
1411 if (m_provider == null)
1413 ThrowEventSourceException();
1417 if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
1418 ThrowEventSourceException();
1422 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1423 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1424 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1425 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1428 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1429 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1431 m_config = ValidateSettings(settings);
1432 Initialize(eventSourceGuid, eventSourceName, traits);
1436 /// This method is responsible for the common initialization path from our constructors. It must
1437 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1438 /// "Log", such an exception would become a cached exception for the initialization of the static
1439 /// member, and any future access to the "Log" would throw the cached exception).
1441 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1442 [SecuritySafeCritical]
1443 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1448 if (m_traits != null && m_traits.Length % 2 != 0)
1449 throw new ArgumentException(Environment.GetResourceString("TraitEven"), "traits");
1451 if (eventSourceGuid == Guid.Empty)
1452 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedGuid"));
1454 if (eventSourceName == null)
1455 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedName"));
1457 m_name = eventSourceName;
1458 m_guid = eventSourceGuid;
1459 #if FEATURE_ACTIVITYSAMPLING
1460 m_curLiveSessions = new SessionMask(0);
1461 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
1462 #endif // FEATURE_ACTIVITYSAMPLING
1464 //Enable Implicit Activity tracker
1465 m_activityTracker = ActivityTracker.Instance;
1467 #if FEATURE_MANAGED_ETW
1468 // Create and register our provider traits. We do this early because it is needed to log errors
1469 // In the self-describing event case.
1470 this.InitializeProviderMetadata();
1472 // Register the provider with ETW
1473 var provider = new OverideEventProvider(this);
1474 provider.Register(eventSourceGuid);
1476 // Add the eventSource to the global (weak) list.
1477 // This also sets m_id, which is the index in the list.
1478 EventListener.AddEventSource(this);
1480 #if FEATURE_MANAGED_ETW
1481 // OK if we get this far without an exception, then we can at least write out error messages.
1482 // Set m_provider, which allows this.
1483 m_provider = provider;
1486 #if !ES_BUILD_STANDALONE
1487 // API available on OS >= Win 8 and patched Win 7.
1488 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1489 var osVer = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor;
1490 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || osVer >= 62)
1493 int setInformationResult;
1494 fixed (void* providerMetadata = this.providerMetadata)
1496 setInformationResult = m_provider.SetInformation(
1497 UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1499 this.providerMetadata.Length);
1503 Contract.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1504 // We are logically completely initialized at this point.
1505 m_completelyInited = true;
1509 if (m_constructionException == null)
1510 m_constructionException = e;
1511 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1514 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1515 lock (EventListener.EventListenersLock)
1517 // If there are any deferred commands, we can do them now.
1518 // This is the most likely place for exceptions to happen.
1519 while (m_deferredCommands != null)
1521 DoCommand(m_deferredCommands); // This can never throw, it catches them and reports the errors.
1522 m_deferredCommands = m_deferredCommands.nextCommand;
1527 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1529 if (eventSourceType == null)
1530 throw new ArgumentNullException("eventSourceType");
1531 Contract.EndContractBlock();
1533 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1534 if (attrib != null && attrib.Name != null)
1537 return eventSourceType.Name;
1541 /// Implements the SHA1 hashing algorithm. Note that this
1542 /// implementation is for hashing public information. Do not
1543 /// use this code to hash private data, as this implementation does
1544 /// not take any steps to avoid information disclosure.
1546 private struct Sha1ForNonSecretPurposes
1548 private long length; // Total message length in bits
1549 private uint[] w; // Workspace
1550 private int pos; // Length of current chunk in bytes
1553 /// Call Start() to initialize the hash object.
1559 this.w = new uint[85];
1564 this.w[80] = 0x67452301;
1565 this.w[81] = 0xEFCDAB89;
1566 this.w[82] = 0x98BADCFE;
1567 this.w[83] = 0x10325476;
1568 this.w[84] = 0xC3D2E1F0;
1572 /// Adds an input byte to the hash.
1574 /// <param name="input">Data to include in the hash.</param>
1575 public void Append(byte input)
1577 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1578 if (64 == ++this.pos)
1585 /// Adds input bytes to the hash.
1587 /// <param name="input">
1588 /// Data to include in the hash. Must not be null.
1590 public void Append(byte[] input)
1592 foreach (var b in input)
1599 /// Retrieves the hash value.
1600 /// Note that after calling this function, the hash object should
1601 /// be considered uninitialized. Subsequent calls to Append or
1602 /// Finish will produce useless results. Call Start() to
1605 /// <param name="output">
1606 /// Buffer to receive the hash value. Must not be null.
1607 /// Up to 20 bytes of hash will be written to the output buffer.
1608 /// If the buffer is smaller than 20 bytes, the remaining hash
1609 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1610 /// rest of the buffer is left unmodified.
1612 public void Finish(byte[] output)
1614 long l = this.length + 8 * this.pos;
1616 while (this.pos != 56)
1623 this.Append((byte)(l >> 56));
1624 this.Append((byte)(l >> 48));
1625 this.Append((byte)(l >> 40));
1626 this.Append((byte)(l >> 32));
1627 this.Append((byte)(l >> 24));
1628 this.Append((byte)(l >> 16));
1629 this.Append((byte)(l >> 8));
1630 this.Append((byte)l);
1632 int end = output.Length < 20 ? output.Length : 20;
1633 for (int i = 0; i != end; i++)
1635 uint temp = this.w[80 + i / 4];
1636 output[i] = (byte)(temp >> 24);
1637 this.w[80 + i / 4] = temp << 8;
1643 /// Called when this.pos reaches 64.
1645 private void Drain()
1647 for (int i = 16; i != 80; i++)
1649 this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1654 uint a = this.w[80];
1655 uint b = this.w[81];
1656 uint c = this.w[82];
1657 uint d = this.w[83];
1658 uint e = this.w[84];
1660 for (int i = 0; i != 20; i++)
1662 const uint k = 0x5A827999;
1663 uint f = (b & c) | ((~b) & d);
1664 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1667 for (int i = 20; i != 40; i++)
1670 const uint k = 0x6ED9EBA1;
1671 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1674 for (int i = 40; i != 60; i++)
1676 uint f = (b & c) | (b & d) | (c & d);
1677 const uint k = 0x8F1BBCDC;
1678 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1681 for (int i = 60; i != 80; i++)
1684 const uint k = 0xCA62C1D6;
1685 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1695 this.length += 512; // 64 bytes == 512 bits
1699 private static uint Rol1(uint input)
1701 return (input << 1) | (input >> 31);
1704 private static uint Rol5(uint input)
1706 return (input << 5) | (input >> 27);
1709 private static uint Rol30(uint input)
1711 return (input << 30) | (input >> 2);
1715 private static Guid GenerateGuidFromName(string name)
1717 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1718 var hash = new Sha1ForNonSecretPurposes();
1720 hash.Append(namespaceBytes);
1722 Array.Resize(ref bytes, 16);
1725 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1726 return new Guid(bytes);
1730 private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1732 IntPtr dataPointer = data->DataPointer;
1733 // advance to next EventData in array
1736 Type dataType = m_eventData[eventId].Parameters[parameterId].ParameterType;
1739 if (dataType == typeof(IntPtr))
1741 return *((IntPtr*)dataPointer);
1743 else if (dataType == typeof(int))
1745 return *((int*)dataPointer);
1747 else if (dataType == typeof(uint))
1749 return *((uint*)dataPointer);
1751 else if (dataType == typeof(long))
1753 return *((long*)dataPointer);
1755 else if (dataType == typeof(ulong))
1757 return *((ulong*)dataPointer);
1759 else if (dataType == typeof(byte))
1761 return *((byte*)dataPointer);
1763 else if (dataType == typeof(sbyte))
1765 return *((sbyte*)dataPointer);
1767 else if (dataType == typeof(short))
1769 return *((short*)dataPointer);
1771 else if (dataType == typeof(ushort))
1773 return *((ushort*)dataPointer);
1775 else if (dataType == typeof(float))
1777 return *((float*)dataPointer);
1779 else if (dataType == typeof(double))
1781 return *((double*)dataPointer);
1783 else if (dataType == typeof(decimal))
1785 return *((decimal*)dataPointer);
1787 else if (dataType == typeof(bool))
1789 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1790 if (*((int*)dataPointer) == 1)
1799 else if (dataType == typeof(Guid))
1801 return *((Guid*)dataPointer);
1803 else if (dataType == typeof(char))
1805 return *((char*)dataPointer);
1807 else if (dataType == typeof(DateTime))
1809 long dateTimeTicks = *((long*)dataPointer);
1810 return DateTime.FromFileTimeUtc(dateTimeTicks);
1812 else if (dataType == typeof(byte[]))
1814 // byte[] are written to EventData* as an int followed by a blob
1815 int cbSize = *((int*)dataPointer);
1816 byte[] blob = new byte[cbSize];
1817 dataPointer = data->DataPointer;
1819 for (int i = 0; i < cbSize; ++i)
1820 blob[i] = *((byte*)dataPointer);
1823 else if (dataType == typeof(byte*))
1830 if (dataType.IsEnum())
1832 dataType = Enum.GetUnderlyingType(dataType);
1836 // Everything else is marshaled as a string.
1837 // ETW strings are NULL-terminated, so marshal everything up to the first
1838 // null in the string.
1839 return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1843 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1845 private EventDispatcher GetDispatcher(EventListener listener)
1847 EventDispatcher dispatcher = m_Dispatchers;
1848 while (dispatcher != null)
1850 if (dispatcher.m_Listener == listener)
1852 dispatcher = dispatcher.m_Next;
1858 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1860 if (m_eventSourceEnabled)
1864 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1865 if (childActivityID != null)
1866 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
1868 #if FEATURE_MANAGED_ETW
1869 if (m_eventData[eventId].EnabledForETW)
1871 Guid* pActivityId = null;
1872 Guid activityId = Guid.Empty;
1873 Guid relatedActivityId = Guid.Empty;
1874 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1875 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1877 if (childActivityID == null &&
1878 ((activityOptions & EventActivityOptions.Disable) == 0))
1880 if (opcode == EventOpcode.Start)
1882 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1884 else if (opcode == EventOpcode.Stop)
1886 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1889 if (activityId != Guid.Empty)
1890 pActivityId = &activityId;
1891 if (relatedActivityId != Guid.Empty)
1892 childActivityID = &relatedActivityId;
1895 #if FEATURE_ACTIVITYSAMPLING
1896 // this code should be kept in [....] with WriteEventWithRelatedActivityIdCore().
1897 SessionMask etwSessions = SessionMask.All;
1898 // only compute etwSessions if there are *any* ETW filters enabled...
1899 if ((ulong)m_curLiveSessions != 0)
1900 etwSessions = GetEtwSessionMask(eventId, childActivityID);
1902 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1904 if (!SelfDescribingEvents)
1906 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1908 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1909 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1910 // synthesize a new one
1911 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1912 ThrowEventSourceException();
1916 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1917 // only some of the ETW sessions will receive this event. Synthesize a new
1918 // Descriptor whose Keywords field will have the appropriate bits set.
1919 var desc = new EventDescriptor(
1920 m_eventData[eventId].Descriptor.EventId,
1921 m_eventData[eventId].Descriptor.Version,
1922 m_eventData[eventId].Descriptor.Channel,
1923 m_eventData[eventId].Descriptor.Level,
1924 m_eventData[eventId].Descriptor.Opcode,
1925 m_eventData[eventId].Descriptor.Task,
1926 unchecked((long)(ulong)etwSessions | origKwd));
1928 if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
1929 ThrowEventSourceException();
1934 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1937 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1939 m_eventData[eventId].Parameters);
1940 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1943 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1945 EventSourceOptions opt = new EventSourceOptions
1947 Keywords = (EventKeywords)unchecked((long)(ulong)etwSessions | origKwd),
1948 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1949 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1952 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1956 if (!SelfDescribingEvents)
1958 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
1959 ThrowEventSourceException();
1963 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1966 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1968 m_eventData[eventId].Parameters);
1969 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1973 EventSourceOptions opt = new EventSourceOptions
1975 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1976 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1977 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1980 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1982 #endif // FEATURE_ACTIVITYSAMPLING
1984 #endif // FEATURE_MANAGED_ETW
1985 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1987 #if !ES_BUILD_STANDALONE
1988 // Maintain old behavior - object identity is preserved
1989 if (AppContextSwitches.PreserveEventListnerObjectIdentity)
1991 WriteToAllListeners(eventId, childActivityID, args);
1994 #endif // !ES_BUILD_STANDALONE
1996 object[] serializedArgs = SerializeEventArgs(eventId, args);
1997 WriteToAllListeners(eventId, childActivityID, serializedArgs);
2001 catch (Exception ex)
2003 if (ex is EventSourceException)
2006 ThrowEventSourceException(ex);
2012 unsafe private object[] SerializeEventArgs(int eventId, object[] args)
2014 TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2015 if (eventTypes == null)
2017 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2019 m_eventData[eventId].Parameters);
2020 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2022 var eventData = new object[eventTypes.typeInfos.Length];
2023 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2025 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2031 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2033 int paramCount = m_eventData[eventId].Parameters.Length;
2034 if (eventDataCount != paramCount)
2036 ReportOutOfBandMessage(Environment.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
2037 paramCount = Math.Min(paramCount, eventDataCount);
2040 object[] args = new object[paramCount];
2042 EventSource.EventData* dataPtr = data;
2043 for (int i = 0; i < paramCount; i++)
2044 args[i] = DecodeObject(eventId, i, ref dataPtr);
2045 WriteToAllListeners(eventId, childActivityID, args);
2048 // helper for writing to all EventListeners attached the current eventSource.
2050 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
2052 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2053 eventCallbackArgs.EventId = eventId;
2054 if (childActivityID != null)
2055 eventCallbackArgs.RelatedActivityId = *childActivityID;
2056 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2057 eventCallbackArgs.Message = m_eventData[eventId].Message;
2058 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2060 DisptachToAllListeners(eventId, childActivityID, eventCallbackArgs);
2064 private unsafe void DisptachToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2066 Exception lastThrownException = null;
2067 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2069 Contract.Assert(dispatcher.m_EventEnabled != null);
2070 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2072 #if FEATURE_ACTIVITYSAMPLING
2073 var activityFilter = dispatcher.m_Listener.m_activityFilter;
2074 // order below is important as PassesActivityFilter will "flow" active activities
2075 // even when the current EventSource doesn't have filtering enabled. This allows
2076 // interesting activities to be updated so that sources that do sample can get
2078 if (activityFilter == null ||
2079 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2080 m_eventData[eventId].TriggersActivityTracking > 0,
2082 !dispatcher.m_activityFilteringEnabled)
2083 #endif // FEATURE_ACTIVITYSAMPLING
2087 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2091 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2092 + e.Message, false);
2093 lastThrownException = e;
2099 if (lastThrownException != null)
2101 throw new EventSourceException(lastThrownException);
2105 [SecuritySafeCritical]
2106 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2107 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2109 if (m_provider != null)
2111 string eventName = "EventSourceMessage";
2112 if (SelfDescribingEvents)
2114 EventSourceOptions opt = new EventSourceOptions
2116 Keywords = (EventKeywords)unchecked(keywords),
2119 var msg = new { message = msgString };
2120 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2121 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2125 // We want the name of the provider to show up so if we don't have a manifest we create
2126 // on that at least has the provider name (I don't define any events).
2127 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2129 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2130 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2131 manifestBuilder.AddEventParameter(typeof(string), "message");
2132 manifestBuilder.EndEvent();
2133 SendManifest(manifestBuilder.CreateManifest());
2136 // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2137 fixed (char* msgStringPtr = msgString)
2139 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2140 EventProvider.EventData data = new EventProvider.EventData();
2141 data.Ptr = (ulong)msgStringPtr;
2142 data.Size = (uint)(2 * (msgString.Length + 1));
2144 m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
2151 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2152 /// while writing the message to any one of the listeners will be silently ignored.
2154 private void WriteStringToAllListeners(string eventName, string msg)
2156 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2157 eventCallbackArgs.EventId = 0;
2158 eventCallbackArgs.Message = msg;
2159 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2160 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2161 eventCallbackArgs.EventName = eventName;
2163 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2165 bool dispatcherEnabled = false;
2166 if (dispatcher.m_EventEnabled == null)
2168 // if the listeners that weren't correctly initialized, we will send to it
2169 // since this is an error message and we want to see it go out.
2170 dispatcherEnabled = true;
2174 // if there's *any* enabled event on the dispatcher we'll write out the string
2175 // otherwise we'll treat the listener as disabled and skip it
2176 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2178 if (dispatcher.m_EventEnabled[evtId])
2180 dispatcherEnabled = true;
2187 if (dispatcherEnabled)
2188 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2192 // ignore any exceptions thrown by listeners' OnEventWritten
2197 #if FEATURE_ACTIVITYSAMPLING
2199 unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
2201 SessionMask etwSessions = new SessionMask();
2203 for (int i = 0; i < SessionMask.MAX; ++i)
2205 EtwSession etwSession = m_etwSessionIdMap[i];
2206 if (etwSession != null)
2208 ActivityFilter activityFilter = etwSession.m_activityFilter;
2209 // PassesActivityFilter() will flow "interesting" activities, so make sure
2210 // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
2211 // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
2212 // do not fire events indiscriminately, when no filters are specified, but only
2213 // if, in addition, the session did not also enable ActivitySampling)
2214 if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
2215 activityFilter != null &&
2216 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2217 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
2218 !m_activityFilteringForETWEnabled[i])
2220 etwSessions[i] = true;
2224 // flow "interesting" activities for all legacy sessions in which there's some
2225 // level of activity tracing enabled (even other EventSources)
2226 if (m_legacySessions != null && m_legacySessions.Count > 0 &&
2227 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
2229 // only calculate InternalCurrentThreadActivityId once
2230 Guid* pCurrentActivityId = null;
2231 Guid currentActivityId;
2232 foreach (var legacyEtwSession in m_legacySessions)
2234 if (legacyEtwSession == null)
2237 ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
2238 if (activityFilter != null)
2240 if (pCurrentActivityId == null)
2242 currentActivityId = InternalCurrentThreadActivityId;
2243 pCurrentActivityId = ¤tActivityId;
2245 ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
2252 #endif // FEATURE_ACTIVITYSAMPLING
2255 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2256 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2258 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2263 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2264 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2266 #if FEATURE_MANAGED_ETW_CHANNELS
2267 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2269 EventChannel channel = EventChannel.None;
2272 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2275 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2276 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2281 // does is pass the level test?
2282 if ((currentLevel != 0) && (currentLevel < eventLevel))
2285 // if yes, does it pass the keywords test?
2286 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2288 #if FEATURE_MANAGED_ETW_CHANNELS
2289 // is there a channel with keywords that match currentMatchAnyKeyword?
2290 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2292 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2293 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2299 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2306 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2307 private void ThrowEventSourceException(Exception innerEx = null)
2309 // If we fail during ouf of band logging we may end up trying
2310 // to throw another EventSourceException, thus hitting a StackOverflowException.
2311 // Avoid StackOverflow by making sure we do not recursively call this method.
2312 if (m_EventSourceExceptionRecurenceCount > 0)
2316 m_EventSourceExceptionRecurenceCount++;
2319 switch (EventProvider.GetLastWriteEventError())
2321 case EventProvider.WriteEventErrorCode.EventTooBig:
2322 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_EventTooBig"), true);
2323 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"), innerEx);
2325 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2326 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true);
2327 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
2329 case EventProvider.WriteEventErrorCode.NullInput:
2330 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NullInput"), true);
2331 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"), innerEx);
2333 case EventProvider.WriteEventErrorCode.TooManyArgs:
2334 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_TooManyArgs"), true);
2335 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_TooManyArgs"), innerEx);
2338 if (innerEx != null)
2339 ReportOutOfBandMessage("EventSourceException: " + innerEx.GetType() + ":" + innerEx.Message, true);
2341 ReportOutOfBandMessage("EventSourceException", true);
2342 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2348 m_EventSourceExceptionRecurenceCount--;
2352 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData)
2354 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2355 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive)
2357 ThrowEventSourceException();
2361 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2363 if (opcode == EventOpcode.Info)
2365 if (eventName.EndsWith(s_ActivityStartSuffix))
2367 return EventOpcode.Start;
2369 else if (eventName.EndsWith(s_ActivityStopSuffix))
2371 return EventOpcode.Stop;
2378 #if FEATURE_MANAGED_ETW
2380 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2382 private class OverideEventProvider : EventProvider
2384 public OverideEventProvider(EventSource eventSource)
2386 this.m_eventSource = eventSource;
2388 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2389 int perEventSourceSessionId, int etwSessionId)
2391 // We use null to represent the ETW EventListener.
2392 EventListener listener = null;
2393 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
2394 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2396 private EventSource m_eventSource;
2401 /// Used to hold all the static information about an event. This includes everything in the event
2402 /// descriptor as well as some stuff we added specifically for EventSource. see the
2403 /// code:m_eventData for where we use this.
2405 internal struct EventMetadata
2407 public EventDescriptor Descriptor;
2408 public EventTags Tags;
2409 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2410 public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
2411 #if !FEATURE_ACTIVITYSAMPLING
2412 #pragma warning disable 0649
2414 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2415 #if !FEATURE_ACTIVITYSAMPLING
2416 #pragma warning restore 0649
2418 public string Name; // the name of the event
2419 public string Message; // If the event has a message associated with it, this is it.
2420 public ParameterInfo[] Parameters; //
2422 public TraceLoggingEventTypes TraceLoggingEventTypes;
2423 public EventActivityOptions ActivityOptions;
2426 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2427 // eventSource. The logic is as follows
2429 // * if Command == Update
2430 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2431 // to (if listener != null)
2432 // perEventSourceSessionId = 0 - reserved for EventListeners
2433 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2434 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2435 // Keywords that identifies the session
2436 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2437 // discriminated by etwSessionId
2438 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2439 // activity tracing across different providers (which might have different sessionIds
2440 // for the same ETW session)
2441 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2442 // eventSource. In particular, if 'enabled' is false, 'level' and
2443 // 'matchAnyKeywords' are not used.
2444 // * OnEventCommand is invoked, which may cause calls to
2445 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2446 // depending on the logic in that routine.
2447 // * else (command != Update)
2448 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2449 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2451 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2452 internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
2453 EventCommand command, bool enable,
2454 EventLevel level, EventKeywords matchAnyKeyword,
2455 IDictionary<string, string> commandArguments)
2457 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2458 lock (EventListener.EventListenersLock)
2460 if (m_completelyInited) // We are fully initialized, do the command
2461 DoCommand(commandArgs);
2464 // We can't do the command, simply remember it and we do it when we are fully constructed.
2465 commandArgs.nextCommand = m_deferredCommands;
2466 m_deferredCommands = commandArgs;
2472 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2473 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2474 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2475 /// This helper actually does all actual command logic.
2477 internal void DoCommand(EventCommandEventArgs commandArgs)
2479 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2480 // We defer commands until we are completely inited. This allows error messages to be sent.
2481 Contract.Assert(m_completelyInited);
2483 if (m_provider == null) // If we failed to construct
2486 m_outOfBandMessageCount = 0;
2487 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2490 EnsureDescriptorsInitialized();
2491 Contract.Assert(m_eventData != null);
2493 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2494 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2495 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2496 throw new ArgumentException(Environment.GetResourceString("EventSource_ListenerNotFound"));
2498 if (commandArgs.Arguments == null)
2499 commandArgs.Arguments = new Dictionary<string, string>();
2501 if (commandArgs.Command == EventCommand.Update)
2503 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2504 for (int i = 0; i < m_eventData.Length; i++)
2505 EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2507 if (commandArgs.enable)
2509 if (!m_eventSourceEnabled)
2511 // EventSource turned on for the first time, simply copy the bits.
2512 m_level = commandArgs.level;
2513 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2517 // Already enabled, make it the most verbose of the existing and new filter
2518 if (commandArgs.level > m_level)
2519 m_level = commandArgs.level;
2520 if (commandArgs.matchAnyKeyword == 0)
2521 m_matchAnyKeyword = 0;
2522 else if (m_matchAnyKeyword != 0)
2523 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2527 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2528 // represent 0-based positive values
2529 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2530 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2531 bSessionEnable = false;
2533 if (commandArgs.listener == null)
2535 if (!bSessionEnable)
2536 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2537 // for "global" enable/disable (passed in with listener == null and
2538 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2539 --commandArgs.perEventSourceSessionId;
2542 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2544 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2546 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2547 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2548 Contract.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2550 // Send the manifest if we are enabling an ETW session
2551 if (bSessionEnable && commandArgs.dispatcher == null)
2553 // eventSourceDispatcher == null means this is the ETW manifest
2555 // Note that we unconditionally send the manifest whenever we are enabled, even if
2556 // we were already enabled. This is because there may be multiple sessions active
2557 // and we can't know that all the sessions have seen the manifest.
2558 if (!SelfDescribingEvents)
2559 SendManifest(m_rawManifest);
2562 #if FEATURE_ACTIVITYSAMPLING
2563 if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2565 bool participateInSampling = false;
2566 string activityFilters;
2569 ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
2570 out activityFilters, out sessionIdBit);
2572 if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
2574 throw new ArgumentException(Environment.GetResourceString("EventSource_SessionIdError",
2575 commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
2576 sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
2579 if (commandArgs.listener == null)
2581 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
2585 ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
2586 commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
2589 else if (!bSessionEnable && commandArgs.listener == null)
2591 // if we disable an ETW session, indicate that in a synthesized command argument
2592 if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
2594 commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
2597 #endif // FEATURE_ACTIVITYSAMPLING
2599 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2600 // things like log messages, or test if keywords are enabled in the callback.
2601 if (commandArgs.enable)
2603 Contract.Assert(m_eventData != null);
2604 m_eventSourceEnabled = true;
2607 this.OnEventCommand(commandArgs);
2609 #if FEATURE_ACTIVITYSAMPLING
2610 if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2612 // if we disable an ETW session, complete disabling it
2613 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
2615 #endif // FEATURE_ACTIVITYSAMPLING
2617 if (!commandArgs.enable)
2619 // If we are disabling, maybe we can turn on 'quick checks' to filter
2620 // quickly. These are all just optimizations (since later checks will still filter)
2622 #if FEATURE_ACTIVITYSAMPLING
2623 // Turn off (and forget) any information about Activity Tracing.
2624 if (commandArgs.listener == null)
2626 // reset all filtering information for activity-tracing-aware sessions
2627 for (int i = 0; i < SessionMask.MAX; ++i)
2629 EtwSession etwSession = m_etwSessionIdMap[i];
2630 if (etwSession != null)
2631 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2633 m_activityFilteringForETWEnabled = new SessionMask(0);
2634 m_curLiveSessions = new SessionMask(0);
2635 // reset activity-tracing-aware sessions
2636 if (m_etwSessionIdMap != null)
2637 for (int i = 0; i < SessionMask.MAX; ++i)
2638 m_etwSessionIdMap[i] = null;
2639 // reset legacy sessions
2640 if (m_legacySessions != null)
2641 m_legacySessions.Clear();
2645 ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
2646 commandArgs.dispatcher.m_activityFilteringEnabled = false;
2648 #endif // FEATURE_ACTIVITYSAMPLING
2650 // There is a good chance EnabledForAnyListener are not as accurate as
2651 // they could be, go ahead and get a better estimate.
2652 for (int i = 0; i < m_eventData.Length; i++)
2654 bool isEnabledForAnyListener = false;
2655 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2657 if (dispatcher.m_EventEnabled[i])
2659 isEnabledForAnyListener = true;
2663 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2666 // If no events are enabled, disable the global enabled bit.
2667 if (!AnyEventEnabled())
2670 m_matchAnyKeyword = 0;
2671 m_eventSourceEnabled = false;
2674 #if FEATURE_ACTIVITYSAMPLING
2675 UpdateKwdTriggers(commandArgs.enable);
2676 #endif // FEATURE_ACTIVITYSAMPLING
2680 if (commandArgs.Command == EventCommand.SendManifest)
2683 if (m_rawManifest != null)
2684 SendManifest(m_rawManifest);
2687 // These are not used for non-update commands and thus should always be 'default' values
2688 // Contract.Assert(enable == true);
2689 // Contract.Assert(level == EventLevel.LogAlways);
2690 // Contract.Assert(matchAnyKeyword == EventKeywords.None);
2692 this.OnEventCommand(commandArgs);
2695 #if FEATURE_ACTIVITYSAMPLING
2696 if (m_completelyInited && (commandArgs.listener != null || shouldReport))
2698 SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
2699 ReportActivitySamplingInfo(commandArgs.listener, m);
2701 #endif // FEATURE_ACTIVITYSAMPLING
2705 // When the ETW session is created after the EventSource has registered with the ETW system
2706 // we can send any error messages here.
2707 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2708 // We never throw when doing a command.
2712 #if FEATURE_ACTIVITYSAMPLING
2714 internal void UpdateEtwSession(
2718 string activityFilters,
2719 bool participateInSampling)
2721 if (sessionIdBit < SessionMask.MAX)
2723 // activity-tracing-aware etw session
2726 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2727 ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
2728 m_etwSessionIdMap[sessionIdBit] = etwSession;
2729 m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
2733 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2734 m_etwSessionIdMap[sessionIdBit] = null;
2735 m_activityFilteringForETWEnabled[sessionIdBit] = false;
2736 if (etwSession != null)
2738 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2739 // the ETW session is going away; remove it from the global list
2740 EtwSession.RemoveEtwSession(etwSession);
2743 m_curLiveSessions[sessionIdBit] = bEnable;
2747 // legacy etw session
2750 if (m_legacySessions == null)
2751 m_legacySessions = new List<EtwSession>(8);
2752 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2753 if (!m_legacySessions.Contains(etwSession))
2754 m_legacySessions.Add(etwSession);
2758 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2759 if (etwSession != null)
2761 if (m_legacySessions != null)
2762 m_legacySessions.Remove(etwSession);
2763 // the ETW session is going away; remove it from the global list
2764 EtwSession.RemoveEtwSession(etwSession);
2770 internal static bool ParseCommandArgs(
2771 IDictionary<string, string> commandArguments,
2772 out bool participateInSampling,
2773 out string activityFilters,
2774 out int sessionIdBit)
2777 participateInSampling = false;
2778 string activityFilterString;
2779 if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
2781 // if a start event is specified default the event source to participate in sampling
2782 participateInSampling = true;
2785 if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
2787 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
2788 activityFilterString == "0")
2789 participateInSampling = false;
2791 participateInSampling = true;
2795 int sessionKwd = -1;
2796 if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
2797 !int.TryParse(sSessionKwd, out sessionKwd) ||
2798 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
2799 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
2806 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
2811 internal void UpdateKwdTriggers(bool enable)
2815 // recompute m_keywordTriggers
2816 ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
2818 gKeywords = 0xFFFFffffFFFFffff;
2820 m_keywordTriggers = 0;
2821 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
2823 EtwSession etwSession = m_etwSessionIdMap[sessId];
2824 if (etwSession == null)
2827 ActivityFilter activityFilter = etwSession.m_activityFilter;
2828 ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
2833 m_keywordTriggers = 0;
2837 #endif // FEATURE_ACTIVITYSAMPLING
2840 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2841 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2842 /// range return false, otherwise true.
2844 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
2846 if (dispatcher == null)
2848 if (eventId >= m_eventData.Length)
2850 #if FEATURE_MANAGED_ETW
2851 if (m_provider != null)
2852 m_eventData[eventId].EnabledForETW = value;
2857 if (eventId >= dispatcher.m_EventEnabled.Length)
2859 dispatcher.m_EventEnabled[eventId] = value;
2861 m_eventData[eventId].EnabledForAnyListener = true;
2867 /// Returns true if any event at all is on.
2869 private bool AnyEventEnabled()
2871 for (int i = 0; i < m_eventData.Length; i++)
2872 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
2877 private bool IsDisposed { get { return m_provider == null || m_provider.m_disposed; } }
2879 [SecuritySafeCritical]
2880 private void EnsureDescriptorsInitialized()
2882 #if !ES_BUILD_STANDALONE
2883 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
2885 if (m_eventData == null)
2887 Contract.Assert(m_rawManifest == null);
2888 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
2889 Contract.Assert(m_eventData != null);
2892 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
2894 EventSource eventSource = eventSourceRef.Target as EventSource;
2895 if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
2897 if (eventSource != this)
2898 throw new ArgumentException(Environment.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
2902 // Make certain all dispatchers also have their arrays initialized
2903 EventDispatcher dispatcher = m_Dispatchers;
2904 while (dispatcher != null)
2906 if (dispatcher.m_EventEnabled == null)
2907 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
2908 dispatcher = dispatcher.m_Next;
2911 if (s_currentPid == 0)
2913 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL
2914 // for non-BCL EventSource we must assert SecurityPermission
2915 new SecurityPermission(PermissionState.Unrestricted).Assert();
2917 s_currentPid = Win32Native.GetCurrentProcessId();
2921 // Send out the ETW manifest XML out to ETW
2922 // Today, we only send the manifest to ETW, custom listeners don't get it.
2923 [SecuritySafeCritical]
2924 private unsafe bool SendManifest(byte[] rawManifest)
2926 bool success = true;
2928 if (rawManifest == null)
2931 Contract.Assert(!SelfDescribingEvents);
2933 #if FEATURE_MANAGED_ETW
2934 fixed (byte* dataPtr = rawManifest)
2936 // we don't want the manifest to show up in the event log channels so we specify as keywords
2937 // everything but the first 8 bits (reserved for the 8 channels)
2938 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
2939 ManifestEnvelope envelope = new ManifestEnvelope();
2941 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
2942 envelope.MajorVersion = 1;
2943 envelope.MinorVersion = 0;
2944 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
2945 int dataLeft = rawManifest.Length;
2946 envelope.ChunkNumber = 0;
2948 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
2949 dataDescrs[0].Ptr = (ulong)&envelope;
2950 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
2951 dataDescrs[0].Reserved = 0;
2953 dataDescrs[1].Ptr = (ulong)dataPtr;
2954 dataDescrs[1].Reserved = 0;
2956 int chunkSize = ManifestEnvelope.MaxChunkSize;
2957 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
2958 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
2959 while (dataLeft > 0)
2961 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
2962 if (m_provider != null)
2964 if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
2966 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
2967 // can fail. If we get this failure on the first chunk try again with something smaller
2968 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
2969 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
2971 if (envelope.ChunkNumber == 0 && chunkSize > 256)
2973 chunkSize = chunkSize / 2;
2974 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
2978 if (ThrowOnEventWriteErrors)
2979 ThrowEventSourceException();
2983 dataLeft -= chunkSize;
2984 dataDescrs[1].Ptr += (uint)chunkSize;
2985 envelope.ChunkNumber++;
2993 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
2995 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
2999 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3000 // When that is the case, we have the build the custom assemblies on a member by hand.
3001 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3003 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3005 // Let the runtime to the work for us, since we can execute code in this context.
3006 Attribute firstAttribute = null;
3007 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3009 firstAttribute = (Attribute)attribute;
3012 return firstAttribute;
3016 // In the reflection only context, we have to do things by hand.
3017 string fullTypeNameToFind = attributeType.FullName;
3019 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3020 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3023 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3025 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
3027 Attribute attr = null;
3029 Contract.Assert(data.ConstructorArguments.Count <= 1);
3031 if (data.ConstructorArguments.Count == 1)
3033 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
3035 else if (data.ConstructorArguments.Count == 0)
3037 attr = (Attribute)Activator.CreateInstance(attributeType);
3042 Type t = attr.GetType();
3044 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3046 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
3047 object value = namedArgument.TypedValue.Value;
3049 if (p.PropertyType.IsEnum)
3051 value = Enum.Parse(p.PropertyType, value.ToString());
3054 p.SetValue(attr, value, null);
3063 #else // ES_BUILD_PCL
3064 throw new ArgumentException(Environment.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
3069 /// Evaluates if two related "EventSource"-domain types should be considered the same
3071 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3072 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3073 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3074 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3076 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3077 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3080 // are these the same type?
3081 attributeType == reflectedAttributeType ||
3082 // are the full typenames equal?
3083 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3084 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3085 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3086 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3087 attributeType.Namespace.EndsWith("Diagnostics.Tracing") &&
3088 (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing")
3089 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3090 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing")
3095 private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3097 // return false for "object" and interfaces
3098 if (eventSourceType.BaseType() == null)
3101 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3104 eventSourceType = eventSourceType.BaseType();
3106 while (eventSourceType != null && eventSourceType.IsAbstract());
3108 if (eventSourceType != null)
3110 if (!allowEventSourceOverride)
3112 if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
3113 !reflectionOnly && eventSourceType != typeof(EventSource))
3118 if (eventSourceType.Name != "EventSource")
3122 return eventSourceType;
3125 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3126 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3127 // at run time. 'source' is the event source to place the descriptors. If it is null,
3128 // then the descriptors are not creaed, and just the manifest is generated.
3129 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
3130 EventManifestOptions flags = EventManifestOptions.None)
3132 ManifestBuilder manifest = null;
3133 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3134 Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3137 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3140 #if DEBUG && ES_BUILD_STANDALONE
3141 TestSupport.TestHooks.MaybeThrow(eventSourceType,
3142 TestSupport.Category.ManifestError,
3143 "EventSource_CreateManifestAndDescriptors",
3144 new ArgumentException("EventSource_CreateManifestAndDescriptors"));
3149 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3150 EventAttribute defaultEventAttribute;
3151 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3152 EventMetadata[] eventData = null;
3153 Dictionary<string, string> eventsByName = null;
3154 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3156 eventData = new EventMetadata[methods.Length + 1];
3157 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3160 // See if we have localization information.
3161 ResourceManager resources = null;
3162 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3163 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3164 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3166 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3169 // Add an entry unconditionally for event ID 0 which will be for a string message.
3170 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3171 manifest.AddEventParameter(typeof(string), "message");
3172 manifest.EndEvent();
3174 // eventSourceType must be sealed and must derive from this EventSource
3175 if ((flags & EventManifestOptions.Strict) != 0)
3177 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3180 manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
3181 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3182 manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
3185 // Collect task, opcode, keyword and channel information
3186 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3187 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3189 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3192 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3193 if (nestedType != null)
3195 if (eventSourceType.IsAbstract())
3197 manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
3201 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3203 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3208 // ensure we have keywords for the session-filtering reserved bits
3210 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3211 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3212 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3213 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3216 if (eventSourceType.Name != "EventSource")
3218 for (int i = 0; i < methods.Length; i++)
3220 MethodInfo method = methods[i];
3221 ParameterInfo[] args = method.GetParameters();
3223 // Get the EventDescriptor (from the Custom attributes)
3224 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3226 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3227 // the only reason of limiting the number of methods considered to be events. This broke a common
3228 // design of having event sources implement specific interfaces. To fix this in a compatible way
3229 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3230 // as they are marked with the [Event] attribute
3231 if (/* method.IsVirtual || */ method.IsStatic)
3236 if (eventSourceType.IsAbstract())
3238 if (eventAttribute != null)
3239 manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
3242 else if (eventAttribute == null)
3244 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3245 // (see Compat comment above)
3246 if (method.ReturnType != typeof(void))
3251 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3252 // (see Compat comment above)
3253 if (method.IsVirtual)
3258 // If we explicitly mark the method as not being an event, then honor that.
3259 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3262 defaultEventAttribute = new EventAttribute(eventId);
3263 eventAttribute = defaultEventAttribute;
3265 else if (eventAttribute.EventId <= 0)
3267 manifest.ManifestError(Environment.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
3268 continue; // don't validate anything else for this event
3270 if (method.Name.LastIndexOf('.') >= 0)
3271 manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
3274 string eventName = method.Name;
3276 if (!eventAttribute.IsOpcodeSet)
3278 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3279 bool noTask = (eventAttribute.Task == EventTask.None);
3280 if (eventAttribute.Task == EventTask.None)
3281 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3283 // pick a default opcode (either Info or start or stop if the name ends with that suffix.
3284 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3286 // Make the stop opcode have the same task as the start opcode.
3289 if (eventAttribute.Opcode == EventOpcode.Start)
3291 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3292 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3293 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3295 // Add a task that is just the task name for the start event. This supress the auto-task generation
3296 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3297 manifest.AddTask(taskName, (int)eventAttribute.Task);
3300 else if (eventAttribute.Opcode == EventOpcode.Stop)
3302 // Find the start associated with this stop event. We require start to be immediately before the stop
3303 int startEventId = eventAttribute.EventId - 1;
3304 if (eventData != null && startEventId < eventData.Length)
3306 Contract.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3307 EventMetadata startEventMetadata = eventData[startEventId];
3309 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3310 // Ideally we would throw an error
3311 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3312 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3313 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3314 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3317 // Make the stop event match the start event
3318 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3322 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3323 throw new ArgumentException(Environment.GetResourceString("EventSource_StopsFollowStarts"));
3328 RemoveFirstArgIfRelatedActivityId(ref args);
3329 if (!(source != null && source.SelfDescribingEvents))
3331 manifest.StartEvent(eventName, eventAttribute);
3332 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3334 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3336 manifest.EndEvent();
3339 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3341 // Do checking for user errors (optional, but not a big deal so we do it).
3342 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest);
3344 #if FEATURE_MANAGED_ETW_CHANNELS
3345 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3346 // and is not required for the manifest
3347 if (eventAttribute.Channel != EventChannel.None)
3351 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel);
3355 string eventKey = "event_" + eventName;
3356 string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3357 // overwrite inline message with the localized message
3358 if (msg != null) eventAttribute.Message = msg;
3360 AddEventDescriptor(ref eventData, eventName, eventAttribute, args);
3365 // Tell the TraceLogging stuff where to start allocating its own IDs.
3366 NameInfo.ReserveEventIDsBelow(eventId);
3370 TrimEventDescriptors(ref eventData);
3371 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3372 #if FEATURE_MANAGED_ETW_CHANNELS
3373 source.m_channelData = manifest.GetChannelData();
3377 // if this is an abstract event source we've already performed all the validation we can
3378 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3380 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3381 #if FEATURE_MANAGED_ETW_CHANNELS
3382 || manifest.GetChannelData().Length > 0
3386 // if the manifest is not needed and we're not requested to validate the event source return early
3387 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3390 res = manifest.CreateManifest();
3395 // if this is a runtime manifest generation let the exception propagate
3396 if ((flags & EventManifestOptions.Strict) == 0)
3398 // else store it to include it in the Argument exception we raise below
3402 if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3404 string msg = String.Empty;
3405 if (manifest.Errors.Count > 0)
3407 bool firstError = true;
3408 foreach (string error in manifest.Errors)
3411 msg += Environment.NewLine;
3417 msg = "Unexpected error: " + exception.Message;
3419 throw new ArgumentException(msg, exception);
3422 return bNeedsManifest ? res : null;
3425 private static void RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3427 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3428 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3429 string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
3431 var newargs = new ParameterInfo[args.Length - 1];
3432 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3437 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3439 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3441 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3442 Type staticFieldType = staticField.FieldType;
3443 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3445 if (providerEnumKind != "Opcodes") goto Error;
3446 int value = (int)staticField.GetRawConstantValue();
3447 manifest.AddOpcode(staticField.Name, value);
3449 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3451 if (providerEnumKind != "Tasks") goto Error;
3452 int value = (int)staticField.GetRawConstantValue();
3453 manifest.AddTask(staticField.Name, value);
3455 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3457 if (providerEnumKind != "Keywords") goto Error;
3458 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3459 manifest.AddKeyword(staticField.Name, value);
3461 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3462 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3464 if (providerEnumKind != "Channels") goto Error;
3465 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3466 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3471 manifest.ManifestError(Environment.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
3474 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3475 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3476 // it is populated if we need to look up message resources
3477 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3478 EventAttribute eventAttribute, ParameterInfo[] eventParameters)
3480 if (eventData == null || eventData.Length <= eventAttribute.EventId)
3482 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3483 Array.Copy(eventData, newValues, eventData.Length);
3484 eventData = newValues;
3487 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3488 eventAttribute.EventId,
3489 eventAttribute.Version,
3490 #if FEATURE_MANAGED_ETW_CHANNELS
3491 (byte)eventAttribute.Channel,
3495 (byte)eventAttribute.Level,
3496 (byte)eventAttribute.Opcode,
3497 (int)eventAttribute.Task,
3498 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3500 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3501 eventData[eventAttribute.EventId].Name = eventName;
3502 eventData[eventAttribute.EventId].Parameters = eventParameters;
3503 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3504 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3507 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3508 // size after all event descriptors have been added.
3509 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3511 int idx = eventData.Length;
3515 if (eventData[idx].Descriptor.EventId != 0)
3518 if (eventData.Length - idx > 2) // allow one wasted slot.
3520 EventMetadata[] newValues = new EventMetadata[idx + 1];
3521 Array.Copy(eventData, newValues, newValues.Length);
3522 eventData = newValues;
3526 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3527 // when a listener gets attached to a eventSource
3528 internal void AddListener(EventListener listener)
3530 lock (EventListener.EventListenersLock)
3532 bool[] enabledArray = null;
3533 if (m_eventData != null)
3534 enabledArray = new bool[m_eventData.Length];
3535 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3536 listener.OnEventSourceCreated(this);
3540 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3541 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3542 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3543 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3544 ManifestBuilder manifest)
3546 int evtId = eventAttribute.EventId;
3547 string evtName = method.Name;
3548 int eventArg = GetHelperCallFirstArg(method);
3549 if (eventArg >= 0 && evtId != eventArg)
3551 manifest.ManifestError(Environment.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
3554 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3556 manifest.ManifestError(Environment.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
3559 // We give a task to things if they don't have one.
3560 Contract.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3561 for (int idx = 0; idx < eventData.Length; ++idx)
3563 // skip unused Event IDs.
3564 if (eventData[idx].Name == null)
3567 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3569 manifest.ManifestError(Environment.GetResourceString("EventSource_TaskOpcodePairReused",
3570 evtName, evtId, eventData[idx].Name, idx));
3574 // for non-default event opcodes the user must define a task!
3575 if (eventAttribute.Opcode != EventOpcode.Info &&
3576 (eventAttribute.Task == EventTask.None || eventAttribute.Task == (EventTask)(0xFFFE - evtId)))
3578 manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
3581 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3582 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3583 // by eventRegister.exe)
3584 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3585 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3587 // throw new WarningException(Environment.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
3590 if (eventsByName == null)
3591 eventsByName = new Dictionary<string, string>();
3593 if (eventsByName.ContainsKey(evtName))
3594 manifest.ManifestError(Environment.GetResourceString("EventSource_EventNameReused", evtName));
3596 eventsByName[evtName] = evtName;
3600 /// This method looks at the IL and tries to pattern match against the standard
3601 /// 'boilerplate' event body
3603 /// { if (Enabled()) WriteEvent(#, ...) }
3605 /// If the pattern matches, it returns the literal number passed as the first parameter to
3606 /// the WriteEvent. This is used to find common user errors (mismatching this
3607 /// number with the EventAttribute ID). It is only used for validation.
3609 /// <param name="method">The method to probe.</param>
3610 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3611 [SecuritySafeCritical]
3612 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3613 static private int GetHelperCallFirstArg(MethodInfo method)
3616 // Currently searches for the following pattern
3618 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3621 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3623 // NOP // 0 or more times
3626 // If we find this pattern we return the XXX. Otherwise we return -1.
3627 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3628 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3630 for (int idx = 0; idx < instrs.Length; )
3632 switch (instrs[idx])
3655 case 21: // LDC_I4_M1
3656 case 22: // LDC_I4_0
3657 case 23: // LDC_I4_1
3658 case 24: // LDC_I4_2
3659 case 25: // LDC_I4_3
3660 case 26: // LDC_I4_4
3661 case 27: // LDC_I4_5
3662 case 28: // LDC_I4_6
3663 case 29: // LDC_I4_7
3664 case 30: // LDC_I4_8
3665 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3666 retVal = instrs[idx] - 22;
3668 case 31: // LDC_I4_S
3669 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3670 retVal = instrs[idx + 1];
3683 // Is this call just before return?
3684 for (int search = idx + 1; search < instrs.Length; search++)
3686 if (instrs[search] == 42) // RET
3688 if (instrs[search] != 0) // NOP
3694 case 44: // BRFALSE_S
3695 case 45: // BRTRUE_S
3704 case 103: // CONV_I1
3705 case 104: // CONV_I2
3706 case 105: // CONV_I4
3707 case 106: // CONV_I8
3708 case 109: // CONV_U4
3709 case 110: // CONV_U8
3715 case 162: // STELEM_REF
3719 // Covers the CEQ instructions used in debug code for some reason.
3720 if (idx >= instrs.Length || instrs[idx] >= 6)
3724 /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3725 " at " + idx + " in method " + method.Name); */
3734 #if false // This routine is not needed at all, it was used for unit test debugging.
3735 [Conditional("DEBUG")]
3736 private static void OutputDebugString(string msg)
3739 msg = msg.TrimEnd('\r', '\n') +
3740 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
3741 System.Diagnostics.Debugger.Log(0, null, msg);
3747 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3748 /// It will do this even if the EventSource is not enabled.
3751 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3752 internal void ReportOutOfBandMessage(string msg, bool flush)
3757 // send message to debugger without delay
3758 System.Diagnostics.Debugger.Log(0, null, msg + "\r\n");
3761 // Send it to all listeners.
3762 if (m_outOfBandMessageCount < 254) // Note this is only if size byte
3763 m_outOfBandMessageCount++;
3766 if (m_outOfBandMessageCount == 255)
3768 m_outOfBandMessageCount = 255; // Mark that we hit the limit. Notify them that this is the case.
3769 msg = "Reached message limit. End of EventSource error messages.";
3772 WriteEventString(EventLevel.LogAlways, -1, msg);
3773 WriteStringToAllListeners("EventSourceMessage", msg);
3775 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3778 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3780 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3781 EventSourceSettings.EtwSelfDescribingEventFormat;
3782 if ((settings & evtFormatMask) == evtFormatMask)
3783 throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidEventFormat"), "settings");
3785 // If you did not explicitly ask for manifest, you get self-describing.
3786 if ((settings & evtFormatMask) == 0)
3787 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3791 private bool ThrowOnEventWriteErrors
3793 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
3796 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
3797 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
3801 private bool SelfDescribingEvents
3805 Contract.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
3806 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
3807 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
3813 m_config |= EventSourceSettings.EtwManifestEventFormat;
3814 m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
3818 m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
3819 m_config &= ~EventSourceSettings.EtwManifestEventFormat;
3824 #if FEATURE_ACTIVITYSAMPLING
3825 private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
3827 Contract.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
3829 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
3831 if (!sessions[perEventSourceSessionId])
3835 if (listener == null)
3837 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
3838 Contract.Assert(etwSession != null);
3839 af = etwSession.m_activityFilter;
3843 af = listener.m_activityFilter;
3849 SessionMask m = new SessionMask();
3850 m[perEventSourceSessionId] = true;
3852 foreach (var t in af.GetFilterAsTuple(m_guid))
3854 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
3857 bool participateInSampling = (listener == null) ?
3858 m_activityFilteringForETWEnabled[perEventSourceSessionId] :
3859 GetDispatcher(listener).m_activityFilteringEnabled;
3860 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
3861 perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
3864 #endif // FEATURE_ACTIVITYSAMPLING
3866 // private instance state
3867 private string m_name; // My friendly name (privided in ctor)
3868 internal int m_id; // A small integer that is unique to this instance.
3869 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
3870 internal volatile EventMetadata[] m_eventData; // None per-event data
3871 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
3873 private EventSourceSettings m_config; // configuration information
3876 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
3877 internal EventLevel m_level; // highest level enabled by any output dispatcher
3878 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
3880 // Dispatching state
3881 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
3882 #if FEATURE_MANAGED_ETW
3883 private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
3885 private bool m_completelyInited; // The EventSource constructor has returned without exception.
3886 private Exception m_constructionException; // If there was an exception construction, this is it
3887 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
3888 private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
3890 private string[] m_traits; // Used to implement GetTraits
3892 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
3894 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
3896 #if FEATURE_MANAGED_ETW_CHANNELS
3897 internal volatile ulong[] m_channelData;
3900 #if FEATURE_ACTIVITYSAMPLING
3901 private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
3902 private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
3903 private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
3904 internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
3905 internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
3906 static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
3907 // Also used to mark that activity tracing is on for some case
3908 #endif // FEATURE_ACTIVITYSAMPLING
3910 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
3911 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
3912 ActivityTracker m_activityTracker;
3913 internal const string s_ActivityStartSuffix = "Start";
3914 internal const string s_ActivityStopSuffix = "Stop";
3916 // used for generating GUID from eventsource name
3917 private static readonly byte[] namespaceBytes = new byte[] {
3918 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
3919 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
3926 /// Enables specifying event source configuration options to be used in the EventSource constructor.
3929 public enum EventSourceSettings
3932 /// This specifies none of the special configuration options should be enabled.
3936 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
3938 ThrowOnEventWriteErrors = 1,
3940 /// Setting this option is a directive to the ETW listener should use manifest-based format when
3941 /// firing events. This is the default option when defining a type derived from EventSource
3942 /// (using the protected EventSource constructors).
3943 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3945 EtwManifestEventFormat = 4,
3947 /// Setting this option is a directive to the ETW listener should use self-describing event format
3948 /// when firing events. This is the default option when creating a new instance of the EventSource
3949 /// type (using the public EventSource constructors).
3950 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3952 EtwSelfDescribingEventFormat = 8,
3956 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
3957 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
3958 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
3959 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
3960 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
3961 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
3964 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
3965 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
3966 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
3967 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
3969 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
3970 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
3972 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
3973 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
3974 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
3975 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
3976 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
3978 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
3979 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
3980 /// that eventSource what events that dispatcher will receive.
3982 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
3983 /// override this method to do something useful with the data.
3985 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
3986 /// invariant associated with this callback is that every eventSource gets exactly one
3987 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
3988 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
3989 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
3993 public abstract class EventListener : IDisposable
3996 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
3999 protected EventListener()
4001 lock (EventListenersLock)
4003 // Disallow creating EventListener reentrancy.
4004 if (s_CreatingListener)
4005 throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
4009 s_CreatingListener = true;
4011 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4012 // Those added sources see this listener.
4013 this.m_Next = s_Listeners;
4016 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4017 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4018 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4019 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4021 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4023 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4025 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4026 EventSource eventSource = eventSourceRef.Target as EventSource;
4027 if (eventSource != null)
4028 eventSource.AddListener(this); // This will cause the OnEventSourceCreated callback to fire.
4035 s_CreatingListener = false;
4040 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4041 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4042 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4043 /// call Dispose when they are done with their logging.
4045 #if ES_BUILD_STANDALONE
4046 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4048 public virtual void Dispose()
4050 lock (EventListenersLock)
4052 Contract.Assert(s_Listeners != null);
4053 if (s_Listeners != null)
4055 if (this == s_Listeners)
4057 EventListener cur = s_Listeners;
4058 s_Listeners = this.m_Next;
4059 RemoveReferencesToListenerInEventSources(cur);
4063 // Find 'this' from the s_Listeners linked list.
4064 EventListener prev = s_Listeners;
4067 EventListener cur = prev.m_Next;
4072 // Found our Listener, remove references to to it in the eventSources
4073 prev.m_Next = cur.m_Next; // Remove entry.
4074 RemoveReferencesToListenerInEventSources(cur);
4084 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4085 // 'cleanup' associated with this object
4088 /// Enable all events from the eventSource identified by 'eventSource' to the current
4089 /// dispatcher that have a verbosity level of 'level' or lower.
4091 /// This call can have the effect of REDUCING the number of events sent to the
4092 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4094 /// This call never has an effect on other EventListeners.
4097 public void EnableEvents(EventSource eventSource, EventLevel level)
4099 EnableEvents(eventSource, level, EventKeywords.None);
4102 /// Enable all events from the eventSource identified by 'eventSource' to the current
4103 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4104 /// matching any of the bits in 'matchAnyKeyword'.
4106 /// This call can have the effect of REDUCING the number of events sent to the
4107 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4108 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4110 /// This call never has an effect on other EventListeners.
4112 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4114 EnableEvents(eventSource, level, matchAnyKeyword, null);
4117 /// Enable all events from the eventSource identified by 'eventSource' to the current
4118 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4119 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4120 /// effect passing additional 'key-value' arguments 'arguments' might have.
4122 /// This call can have the effect of REDUCING the number of events sent to the
4123 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4124 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4126 /// This call never has an effect on other EventListeners.
4128 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
4130 if (eventSource == null)
4132 throw new ArgumentNullException("eventSource");
4134 Contract.EndContractBlock();
4136 eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4139 /// Disables all events coming from eventSource identified by 'eventSource'.
4141 /// This call never has an effect on other EventListeners.
4143 public void DisableEvents(EventSource eventSource)
4145 if (eventSource == null)
4147 throw new ArgumentNullException("eventSource");
4149 Contract.EndContractBlock();
4151 eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4155 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4156 /// This can happen for all existing EventSources when the EventListener is created
4157 /// as well as for any EventSources that come into existence after the EventListener
4158 /// has been created.
4160 /// These 'catch up' events are called during the construction of the EventListener.
4161 /// Subclasses need to be prepared for that.
4163 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4164 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4166 /// <param name="eventSource"></param>
4167 internal protected virtual void OnEventSourceCreated(EventSource eventSource) { }
4169 /// This method is called whenever an event has been written by a EventSource for which
4170 /// the EventListener has enabled events.
4172 internal protected abstract void OnEventWritten(EventWrittenEventArgs eventData);
4174 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4175 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4176 /// it useful to store additional information about each eventSource connected to it,
4177 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4178 /// (growable) array (eg List(T)).
4180 static protected int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4184 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4185 /// ID to the eventSource (which is simply the ordinal in the global list).
4187 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4188 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4189 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4190 /// that are in the list). This seems OK since the expectation is that EventSources
4191 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4192 /// global variables).
4194 /// <param name="newEventSource"></param>
4195 internal static void AddEventSource(EventSource newEventSource)
4197 lock (EventListenersLock)
4199 if (s_EventSources == null)
4200 s_EventSources = new List<WeakReference>(2);
4202 if (!s_EventSourceShutdownRegistered)
4204 s_EventSourceShutdownRegistered = true;
4205 #if !ES_BUILD_PCL && !FEATURE_CORECLR
4206 AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
4207 AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
4212 // Periodically search the list for existing entries to reuse, this avoids
4213 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4215 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4217 int i = s_EventSources.Count; // Work from the top down.
4221 WeakReference weakRef = s_EventSources[i];
4222 if (!weakRef.IsAlive)
4225 weakRef.Target = newEventSource;
4232 newIndex = s_EventSources.Count;
4233 s_EventSources.Add(new WeakReference(newEventSource));
4235 newEventSource.m_id = newIndex;
4237 // Add every existing dispatcher to the new EventSource
4238 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4239 newEventSource.AddListener(listener);
4245 // Whenver we have async callbacks from native code, there is an ugly issue where
4246 // during .NET shutdown native code could be calling the callback, but the CLR
4247 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4248 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4249 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4250 // do this. This is what this callback is for.
4251 // See bug 724140 for more
4252 private static void DisposeOnShutdown(object sender, EventArgs e)
4254 foreach (var esRef in s_EventSources)
4256 EventSource es = esRef.Target as EventSource;
4263 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4264 /// eventSources in the appdomain.
4266 /// The EventListenersLock must be held before calling this routine.
4268 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4270 // Foreach existing EventSource in the appdomain
4271 foreach (WeakReference eventSourceRef in s_EventSources)
4273 EventSource eventSource = eventSourceRef.Target as EventSource;
4274 if (eventSource != null)
4276 // Is the first output dispatcher the dispatcher we are removing?
4277 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4278 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4281 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4282 EventDispatcher prev = eventSource.m_Dispatchers;
4285 EventDispatcher cur = prev.m_Next;
4288 Contract.Assert(false, "EventSource did not have a registered EventListener!");
4291 if (cur.m_Listener == listenerToRemove)
4293 prev.m_Next = cur.m_Next; // Remove entry.
4304 /// Checks internal consistency of EventSources/Listeners.
4306 [Conditional("DEBUG")]
4307 internal static void Validate()
4309 lock (EventListenersLock)
4311 // Get all listeners
4312 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4313 EventListener cur = s_Listeners;
4316 allListeners.Add(cur, true);
4320 // For all eventSources
4322 foreach (WeakReference eventSourceRef in s_EventSources)
4325 EventSource eventSource = eventSourceRef.Target as EventSource;
4326 if (eventSource == null)
4328 Contract.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4330 // None listeners on eventSources exist in the dispatcher list.
4331 EventDispatcher dispatcher = eventSource.m_Dispatchers;
4332 while (dispatcher != null)
4334 Contract.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4335 dispatcher = dispatcher.m_Next;
4338 // Every dispatcher is on Dispatcher List of every eventSource.
4339 foreach (EventListener listener in allListeners.Keys)
4341 dispatcher = eventSource.m_Dispatchers;
4344 Contract.Assert(dispatcher != null, "Listener is not on all eventSources.");
4345 if (dispatcher.m_Listener == listener)
4347 dispatcher = dispatcher.m_Next;
4355 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4356 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4357 /// the lock object)
4359 internal static object EventListenersLock
4363 if (s_EventSources == null)
4364 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4365 return s_EventSources;
4370 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
4371 #if FEATURE_ACTIVITYSAMPLING
4372 internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
4373 #endif // FEATURE_ACTIVITYSAMPLING
4378 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4379 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4381 internal static EventListener s_Listeners;
4383 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4384 /// remove themselves from this list this is a weak list and the GC that removes them may
4385 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4386 /// to filter those out.
4388 internal static List<WeakReference> s_EventSources;
4391 /// Used to disallow reentrancy.
4393 private static bool s_CreatingListener = false;
4396 /// Used to register AD/Process shutdown callbacks.
4398 private static bool s_EventSourceShutdownRegistered = false;
4403 /// Passed to the code:EventSource.OnEventCommand callback
4405 public class EventCommandEventArgs : EventArgs
4408 /// Gets the command for the callback.
4410 public EventCommand Command { get; internal set; }
4413 /// Gets the arguments for the callback.
4415 public IDictionary<String, String> Arguments { get; internal set; }
4418 /// Enables the event that has the specified identifier.
4420 /// <param name="eventId">Event ID of event to be enabled</param>
4421 /// <returns>true if eventId is in range</returns>
4422 public bool EnableEvent(int eventId)
4424 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4425 throw new InvalidOperationException();
4426 return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
4430 /// Disables the event that have the specified identifier.
4432 /// <param name="eventId">Event ID of event to be disabled</param>
4433 /// <returns>true if eventId is in range</returns>
4434 public bool DisableEvent(int eventId)
4436 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4437 throw new InvalidOperationException();
4438 return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
4443 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4444 EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4446 this.Command = command;
4447 this.Arguments = arguments;
4448 this.eventSource = eventSource;
4449 this.listener = listener;
4450 this.perEventSourceSessionId = perEventSourceSessionId;
4451 this.etwSessionId = etwSessionId;
4452 this.enable = enable;
4454 this.matchAnyKeyword = matchAnyKeyword;
4457 internal EventSource eventSource;
4458 internal EventDispatcher dispatcher;
4460 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4461 internal EventListener listener;
4462 internal int perEventSourceSessionId;
4463 internal int etwSessionId;
4464 internal bool enable;
4465 internal EventLevel level;
4466 internal EventKeywords matchAnyKeyword;
4467 internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
4473 /// EventWrittenEventArgs is passed to the user-provided override for
4474 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4476 public class EventWrittenEventArgs : EventArgs
4479 /// The name of the event.
4481 public string EventName
4485 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4490 return m_eventSource.m_eventData[EventId].Name;
4494 m_eventName = value;
4499 /// Gets the event ID for the event that was written.
4501 public int EventId { get; internal set; }
4504 /// Gets the activity ID for the thread on which the event was written.
4506 public Guid ActivityId
4508 [System.Security.SecurityCritical]
4509 get { return EventSource.CurrentThreadActivityId; }
4513 /// Gets the related activity ID if one was specified when the event was written.
4515 public Guid RelatedActivityId
4517 [System.Security.SecurityCritical]
4523 /// Gets the payload for the event.
4525 public ReadOnlyCollection<Object> Payload { get; internal set; }
4528 /// Gets the payload argument names.
4530 public ReadOnlyCollection<string> PayloadNames
4534 // For contract based events we create the list lazily.
4535 if (m_payloadNames == null)
4537 // Self described events are identified by id -1.
4538 Contract.Assert(EventId != -1);
4540 var names = new List<string>();
4541 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4543 names.Add(parameter.Name);
4545 m_payloadNames = new ReadOnlyCollection<string>(names);
4548 return m_payloadNames;
4553 m_payloadNames = value;
4558 /// Gets the event source object.
4560 public EventSource EventSource { get { return m_eventSource; } }
4563 /// Gets the keywords for the event.
4565 public EventKeywords Keywords
4569 if (EventId < 0) // TraceLogging convention EventID == -1
4572 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4577 /// Gets the operation code for the event.
4579 public EventOpcode Opcode
4583 if (EventId < 0) // TraceLogging convention EventID == -1
4585 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4590 /// Gets the task for the event.
4592 public EventTask Task
4596 if (EventId < 0) // TraceLogging convention EventID == -1
4597 return EventTask.None;
4599 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4604 /// Any provider/user defined options associated with the event.
4606 public EventTags Tags
4610 if (EventId < 0) // TraceLogging convention EventID == -1
4612 return m_eventSource.m_eventData[EventId].Tags;
4617 /// Gets the message for the event.
4619 public string Message
4623 if (EventId < 0) // TraceLogging convention EventID == -1
4626 return m_eventSource.m_eventData[EventId].Message;
4635 #if FEATURE_MANAGED_ETW_CHANNELS
4637 /// Gets the channel for the event.
4639 public EventChannel Channel
4643 if (EventId < 0) // TraceLogging convention EventID == -1
4644 return EventChannel.None;
4645 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4651 /// Gets the version of the event.
4657 if (EventId < 0) // TraceLogging convention EventID == -1
4659 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4664 /// Gets the level for the event.
4666 public EventLevel Level
4671 return EventLevel.LogAlways;
4672 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4677 internal EventWrittenEventArgs(EventSource eventSource)
4679 m_eventSource = eventSource;
4681 private string m_message;
4682 private string m_eventName;
4683 private EventSource m_eventSource;
4684 private ReadOnlyCollection<string> m_payloadNames;
4685 internal EventTags m_tags;
4686 internal EventOpcode m_opcode;
4687 internal EventKeywords m_keywords;
4692 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4694 [AttributeUsage(AttributeTargets.Class)]
4695 public sealed class EventSourceAttribute : Attribute
4698 /// Overrides the ETW name of the event source (which defaults to the class name)
4700 public string Name { get; set; }
4703 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
4704 /// except when upgrading existing ETW providers to using event sources.
4706 public string Guid { get; set; }
4710 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
4711 /// can be localized to several languages if desired. This works by creating a ResX style string table
4712 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
4713 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
4714 /// resources. This name is the value of the LocalizationResources property.
4716 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
4717 /// using the following resource naming scheme
4719 /// <para>* event_EVENTNAME</para>
4720 /// <para>* task_TASKNAME</para>
4721 /// <para>* keyword_KEYWORDNAME</para>
4722 /// <para>* map_MAPNAME</para>
4724 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
4725 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
4726 /// which represent the payload values.
4729 public string LocalizationResources { get; set; }
4733 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
4734 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
4735 /// name of the method and its signature to generate basic schema information for the event. The
4736 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
4739 [AttributeUsage(AttributeTargets.Method)]
4740 public sealed class EventAttribute : Attribute
4742 /// <summary>Construct an EventAttribute with specified eventId</summary>
4743 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
4744 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
4745 /// <summary>Event's ID</summary>
4746 public int EventId { get; private set; }
4747 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
4748 public EventLevel Level { get; set; }
4749 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
4750 public EventKeywords Keywords { get; set; }
4751 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
4752 public EventOpcode Opcode
4760 this.m_opcode = value;
4761 this.m_opcodeSet = true;
4765 internal bool IsOpcodeSet
4773 /// <summary>Event's task: allows logical grouping of events</summary>
4774 public EventTask Task { get; set; }
4775 #if FEATURE_MANAGED_ETW_CHANNELS
4776 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
4777 public EventChannel Channel { get; set; }
4779 /// <summary>Event's version</summary>
4780 public byte Version { get; set; }
4783 /// This can be specified to enable formatting and localization of the event's payload. You can
4784 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
4785 /// with the 'ToString()' of the corresponding part of the event payload.
4787 public string Message { get; set; }
4790 /// User defined options associated with the event. These do not have meaning to the EventSource but
4791 /// are passed through to listeners which given them semantics.
4793 public EventTags Tags { get; set; }
4796 /// Allows fine control over the Activity IDs generated by start and stop events
4798 public EventActivityOptions ActivityOptions { get; set; }
4801 EventOpcode m_opcode;
4802 private bool m_opcodeSet;
4807 /// By default all instance methods in a class that subclasses code:EventSource that and return
4808 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
4809 /// the code:NonEventAttribute
4811 [AttributeUsage(AttributeTargets.Method)]
4812 public sealed class NonEventAttribute : Attribute
4815 /// Constructs a default NonEventAttribute
4817 public NonEventAttribute() { }
4821 #if FEATURE_MANAGED_ETW_CHANNELS
4823 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
4824 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
4826 /// public static class Channels
4828 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
4829 /// public const EventChannel Admin = (EventChannel)16;
4831 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
4832 /// public const EventChannel Operational = (EventChannel)17;
4836 [AttributeUsage(AttributeTargets.Field)]
4837 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4840 class EventChannelAttribute : Attribute
4843 /// Specified whether the channel is enabled by default
4845 public bool Enabled { get; set; }
4848 /// Legal values are in EventChannelType
4850 public EventChannelType EventChannelType { get; set; }
4852 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4854 /// Specifies the isolation for the channel
4856 public EventChannelIsolation Isolation { get; set; }
4859 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
4860 /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
4862 public string Access { get; set; }
4865 /// Allows importing channels defined in external manifests
4867 public string ImportChannel { get; set; }
4875 /// Allowed channel types
4877 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4880 enum EventChannelType
4882 /// <summary>The admin channel</summary>
4884 /// <summary>The operational channel</summary>
4886 /// <summary>The Analytic channel</summary>
4888 /// <summary>The debug channel</summary>
4892 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4894 /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
4895 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
4896 /// access permissions for the channel and backing file.
4899 enum EventChannelIsolation
4902 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
4906 /// All channels that specify System isolation use the same ETW session
4910 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
4911 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
4912 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
4920 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
4922 public enum EventCommand
4925 /// Update EventSource state
4929 /// Request EventSource to generate and send its manifest
4943 #region private classes
4945 #if FEATURE_ACTIVITYSAMPLING
4948 /// ActivityFilter is a helper structure that is used to keep track of run-time state
4949 /// associated with activity filtering. It is 1-1 with EventListeners (logically
4950 /// every listener has one of these, however we actually allocate them lazily), as well
4951 /// as 1-to-1 with tracing-aware EtwSessions.
4953 /// This structure also keeps track of the sampling counts associated with 'trigger'
4954 /// events. Because these trigger events are rare, and you typically only have one of
4955 /// them, we store them here as a linked list.
4957 internal sealed class ActivityFilter : IDisposable
4960 /// Disable all activity filtering for the listener associated with 'filterList',
4961 /// (in the session associated with it) that is triggered by any event in 'source'.
4963 public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
4965 #if !ES_BUILD_STANDALONE
4966 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4969 if (filterList == null)
4973 // Remove it from anywhere in the list (except the first element, which has to
4974 // be treated specially)
4975 ActivityFilter prev = filterList;
4979 if (cur.m_providerGuid == source.Guid)
4981 // update TriggersActivityTracking bit
4982 if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
4983 --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
4985 // Remove it from the linked list.
4986 prev.m_next = cur.m_next;
4987 // dispose of the removed node
5000 // Sadly we have to treat the first element specially in linked list removal in C#
5001 if (filterList.m_providerGuid == source.Guid)
5003 // update TriggersActivityTracking bit
5004 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
5005 --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
5007 // We are the first element in the list.
5008 var first = filterList;
5009 filterList = first.m_next;
5010 // dispose of the removed node
5013 // the above might have removed the one ActivityFilter in the session that contains the
5014 // cleanup delegate; re-create the delegate if needed
5015 if (filterList != null)
5017 EnsureActivityCleanupDelegate(filterList);
5022 /// Currently this has "override" semantics. We first disable all filters
5023 /// associated with 'source', and next we add new filters for each entry in the
5024 /// string 'startEvents'. participateInSampling specifies whether non-startEvents
5025 /// always trigger or only trigger when current activity is 'active'.
5027 public static void UpdateFilter(
5028 ref ActivityFilter filterList,
5030 int perEventSourceSessionId,
5033 #if !ES_BUILD_STANDALONE
5034 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5037 // first remove all filters associated with 'source'
5038 DisableFilter(ref filterList, source);
5040 if (!string.IsNullOrEmpty(startEvents))
5042 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
5043 // The Event may be specified by name or by ID. Errors in parsing such a pair
5044 // result in the error being reported to the listeners, and the pair being ignored.
5045 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
5046 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
5047 // we should initiate activity tracing once every 10 events.
5048 string[] activityFilterStrings = startEvents.Split(' ');
5050 for (int i = 0; i < activityFilterStrings.Length; ++i)
5052 string activityFilterString = activityFilterStrings[i];
5055 int colonIdx = activityFilterString.IndexOf(':');
5058 source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
5059 activityFilterString, false);
5060 // ignore failure...
5063 string sFreq = activityFilterString.Substring(colonIdx + 1);
5064 if (!int.TryParse(sFreq, out sampleFreq))
5066 source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
5069 activityFilterString = activityFilterString.Substring(0, colonIdx);
5070 if (!int.TryParse(activityFilterString, out eventId))
5074 // see if it's an event name
5075 for (int j = 0; j < source.m_eventData.Length; j++)
5077 EventSource.EventMetadata[] ed = source.m_eventData;
5078 if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
5079 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
5081 eventId = ed[j].Descriptor.EventId;
5086 if (eventId < 0 || eventId >= source.m_eventData.Length)
5088 source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
5091 EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
5097 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
5099 public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
5101 for (var af = filterList; af != null; af = af.m_next)
5103 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
5110 /// Returns a session mask representing all sessions in which the activity
5111 /// associated with the current thread is allowed through the activity filter.
5112 /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
5113 /// most of the time this is false as you can guarentee this event is NOT a
5114 /// triggering event. If 'triggeringEvent' is true, then it checks the
5115 /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
5116 /// a trigger. If so it activates the current activity.
5118 /// If 'childActivityID' is present, it will be added to the active set if the
5119 /// current activity is active.
5122 unsafe public static bool PassesActivityFilter(
5123 ActivityFilter filterList,
5124 Guid* childActivityID,
5125 bool triggeringEvent,
5129 Contract.Assert(filterList != null && filterList.m_activeActivities != null);
5130 bool shouldBeLogged = false;
5131 if (triggeringEvent)
5133 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5135 if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
5137 // Update the sampling count with wrap-around
5138 int curSampleCount, newSampleCount;
5141 curSampleCount = af.m_curSampleCount;
5142 if (curSampleCount <= 1)
5143 newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
5145 newSampleCount = curSampleCount - 1;
5147 while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
5148 // If we hit zero, then start tracking the activity.
5149 if (curSampleCount <= 1)
5151 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5152 Tuple<Guid, int> startId;
5153 // only add current activity if it's not already a root activity
5154 if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
5156 // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
5157 shouldBeLogged = true;
5158 af.m_activeActivities[currentActivityId] = Environment.TickCount;
5159 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
5164 // a start event following a triggering start event
5165 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5166 Tuple<Guid, int> startId;
5167 // only remove current activity if we added it
5168 if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
5169 startId.Item1 == source.Guid && startId.Item2 == eventId)
5171 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
5172 // remove activity only from current logging scope (af)
5174 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
5182 var activeActivities = GetActiveActivities(filterList);
5183 if (activeActivities != null)
5185 // if we hadn't already determined this should be logged, test further
5186 if (!shouldBeLogged)
5188 shouldBeLogged = !activeActivities.IsEmpty &&
5189 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
5191 if (shouldBeLogged && childActivityID != null &&
5192 ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
5194 FlowActivityIfNeeded(filterList, null, childActivityID);
5195 // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
5198 // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
5199 return shouldBeLogged;
5202 [System.Security.SecuritySafeCritical]
5203 public static bool IsCurrentActivityActive(ActivityFilter filterList)
5205 var activeActivities = GetActiveActivities(filterList);
5206 if (activeActivities != null &&
5207 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
5214 /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
5215 /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
5216 /// value for 'currentActivityid' is an indication tha caller has already verified
5217 /// that the current activity is active.
5220 unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
5222 Contract.Assert(childActivityID != null);
5224 var activeActivities = GetActiveActivities(filterList);
5225 Contract.Assert(activeActivities != null);
5227 // take currentActivityId == null to mean we *know* the current activity is "active"
5228 if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
5231 if (activeActivities.Count > MaxActivityTrackCount)
5233 TrimActiveActivityStore(activeActivities);
5234 // make sure current activity is still in the set:
5235 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
5237 // add child activity to list of actives
5238 activeActivities[*childActivityID] = Environment.TickCount;
5244 public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
5246 for (var af = activityFilter; af != null; af = af.m_next)
5248 if ((sourceGuid == af.m_providerGuid) &&
5249 (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
5250 ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
5252 // we could be more precise here, if we tracked 'anykeywords' per session
5255 source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
5262 /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
5263 /// associated with 'this' ActivityFilter list, return configured sequence of
5264 /// [eventId, sampleFreq] pairs that defines the sampling policy.
5266 public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
5268 for (ActivityFilter af = this; af != null; af = af.m_next)
5270 if (af.m_providerGuid == sourceGuid)
5271 yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
5276 /// The cleanup being performed consists of removing the m_myActivityDelegate from
5277 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
5279 public void Dispose()
5281 #if !ES_BUILD_STANDALONE
5282 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5284 // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
5285 // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
5286 // during the finalization of the ActivityFilter
5287 if (m_myActivityDelegate != null)
5289 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
5290 m_myActivityDelegate = null;
5297 /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
5298 /// 'samplingFreq' times the event fires. You can have several of these forming a
5301 private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
5303 m_providerGuid = source.Guid;
5304 m_perEventSourceSessionId = perEventSourceSessionId;
5305 m_eventId = eventId;
5306 m_samplingFreq = samplingFreq;
5307 m_next = existingFilter;
5309 Contract.Assert(existingFilter == null ||
5310 (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
5312 // if this is the first filter we add for this session, we need to create a new
5313 // table of activities. m_activeActivities is common across EventSources in the same
5315 ConcurrentDictionary<Guid, int> activeActivities = null;
5316 if (existingFilter == null ||
5317 (activeActivities = GetActiveActivities(existingFilter)) == null)
5319 m_activeActivities = new ConcurrentDictionary<Guid, int>();
5320 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
5322 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
5323 m_myActivityDelegate = GetActivityDyingDelegate(this);
5324 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
5328 m_activeActivities = activeActivities;
5329 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
5335 /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
5336 /// activity-removing delegate for the listener/session associated with 'filterList'.
5338 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
5340 if (filterList == null)
5343 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5345 if (af.m_myActivityDelegate != null)
5349 // we didn't find a delegate
5350 filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
5351 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
5355 /// Builds the delegate to be called when an activity is dying. This is responsible
5356 /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
5357 /// This gets "added" to EventSource.s_activityDying and ends up being called from
5358 /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
5360 /// <returns>The delegate to be called when an activity is dying</returns>
5361 private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
5363 return (Guid oldActivity) =>
5366 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
5367 Tuple<Guid, int> dummyTuple;
5368 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
5373 /// Enables activity filtering for the listener associated with 'filterList', triggering on
5374 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
5376 /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
5377 /// activitySampling if something else triggered).
5379 /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
5380 private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
5382 #if !ES_BUILD_STANDALONE
5383 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5385 Contract.Assert(samplingFreq > 0);
5386 Contract.Assert(eventId >= 0);
5388 filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
5390 // Mark the 'quick Check' that indicates this is a trigger event.
5391 // If eventId is out of range then this mark is not done which has the effect of ignoring
5393 if (0 <= eventId && eventId < source.m_eventData.Length)
5394 ++source.m_eventData[eventId].TriggersActivityTracking;
5400 /// Normally this code never runs, it is here just to prevent run-away resource usage.
5402 private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
5404 if (activities.Count > MaxActivityTrackCount)
5406 // Remove half of the oldest activity ids.
5407 var keyValues = activities.ToArray();
5408 var tickNow = Environment.TickCount;
5410 // Sort by age, taking into account wrap-around. As long as x and y are within
5411 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
5412 // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
5413 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
5414 for (int i = 0; i < keyValues.Length / 2; i++)
5417 activities.TryRemove(keyValues[i].Key, out dummy);
5422 private static ConcurrentDictionary<Guid, int> GetActiveActivities(
5423 ActivityFilter filterList)
5425 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5427 if (af.m_activeActivities != null)
5428 return af.m_activeActivities;
5433 // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
5434 // in the m_next list. The 'int' value in the m_activities set is a timestamp
5435 // (Environment.TickCount) of when the entry was put in the system and is used to
5436 // remove 'old' entries that if the set gets too big.
5437 ConcurrentDictionary<Guid, int> m_activeActivities;
5439 // m_rootActiveActivities holds the "root" active activities, i.e. the activities
5440 // that were marked as active because a Start event fired on them. We need to keep
5441 // track of these to enable sampling in the scenario of an app's main thread that
5442 // never explicitly sets distinct activity IDs as it executes. To handle these
5443 // situations we manufacture a Guid from the thread's ID, and:
5444 // (a) we consider the firing of a start event when the sampling counter reaches
5445 // zero to mark the beginning of an interesting activity, and
5446 // (b) we consider the very next firing of the same start event to mark the
5447 // ending of that activity.
5448 // We use a ConcurrentDictionary to avoid taking explicit locks.
5449 // The key (a guid) represents the activity ID of the root active activity
5450 // The value is made up of the Guid of the event provider and the eventId of
5452 ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
5453 Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
5454 int m_eventId; // triggering event
5455 int m_samplingFreq; // Counter reset to this when it hits 0
5456 int m_curSampleCount; // We count down to 0 and then activate the activity.
5457 int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
5459 const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
5461 ActivityFilter m_next; // We create a linked list of these
5462 Action<Guid> m_myActivityDelegate;
5468 /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
5469 /// are limited to 8 concurrent sessions per machine (currently) we're going to store
5470 /// the active ones in a singly linked list.
5472 internal class EtwSession
5474 public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
5476 if (etwSessionId < 0)
5479 EtwSession etwSession;
5480 foreach (var wrEtwSession in s_etwSessions)
5482 #if ES_BUILD_STANDALONE
5483 if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
5486 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
5491 if (!bCreateIfNeeded)
5494 #if ES_BUILD_STANDALONE
5495 if (s_etwSessions == null)
5496 s_etwSessions = new List<WeakReference>();
5498 etwSession = new EtwSession(etwSessionId);
5499 s_etwSessions.Add(new WeakReference(etwSession));
5501 if (s_etwSessions == null)
5502 s_etwSessions = new List<WeakReference<EtwSession>>();
5504 etwSession = new EtwSession(etwSessionId);
5505 s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
5508 if (s_etwSessions.Count > s_thrSessionCount)
5515 public static void RemoveEtwSession(EtwSession etwSession)
5517 Contract.Assert(etwSession != null);
5518 if (s_etwSessions == null || etwSession == null)
5521 s_etwSessions.RemoveAll((wrEtwSession) =>
5524 #if ES_BUILD_STANDALONE
5525 return (session = (EtwSession) wrEtwSession.Target) != null &&
5526 (session.m_etwSessionId == etwSession.m_etwSessionId);
5528 return wrEtwSession.TryGetTarget(out session) &&
5529 (session.m_etwSessionId == etwSession.m_etwSessionId);
5533 if (s_etwSessions.Count > s_thrSessionCount)
5537 private static void TrimGlobalList()
5539 if (s_etwSessions == null)
5542 s_etwSessions.RemoveAll((wrEtwSession) =>
5544 #if ES_BUILD_STANDALONE
5545 return wrEtwSession.Target == null;
5548 return !wrEtwSession.TryGetTarget(out session);
5553 private EtwSession(int etwSessionId)
5555 m_etwSessionId = etwSessionId;
5558 public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
5559 public ActivityFilter m_activityFilter; // all filters enabled for this session
5561 #if ES_BUILD_STANDALONE
5562 private static List<WeakReference> s_etwSessions = new List<WeakReference>();
5564 private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
5566 private const int s_thrSessionCount = 16;
5569 #endif // FEATURE_ACTIVITYSAMPLING
5571 // holds a bitfield representing a session mask
5573 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5574 /// is the index in the SessionMask of the bit that will be set. These can translate to
5575 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5576 /// FromEventKeywords() methods.
5578 internal struct SessionMask
5580 public SessionMask(SessionMask m)
5581 { m_mask = m.m_mask; }
5583 public SessionMask(uint mask = 0)
5584 { m_mask = mask & MASK; }
5586 public bool IsEqualOrSupersetOf(SessionMask m)
5588 return (this.m_mask | m.m_mask) == this.m_mask;
5591 public static SessionMask All
5593 get { return new SessionMask(MASK); }
5596 public static SessionMask FromId(int perEventSourceSessionId)
5598 Contract.Assert(perEventSourceSessionId < MAX);
5599 return new SessionMask((uint)1 << perEventSourceSessionId);
5602 public ulong ToEventKeywords()
5604 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5607 public static SessionMask FromEventKeywords(ulong m)
5609 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5612 public bool this[int perEventSourceSessionId]
5616 Contract.Assert(perEventSourceSessionId < MAX);
5617 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5621 Contract.Assert(perEventSourceSessionId < MAX);
5622 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5623 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5627 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5629 return new SessionMask(m1.m_mask | m2.m_mask);
5632 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5634 return new SessionMask(m1.m_mask & m2.m_mask);
5637 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5639 return new SessionMask(m1.m_mask ^ m2.m_mask);
5642 public static SessionMask operator ~(SessionMask m)
5644 return new SessionMask(MASK & ~(m.m_mask));
5647 public static explicit operator ulong(SessionMask m)
5648 { return m.m_mask; }
5650 public static explicit operator uint(SessionMask m)
5651 { return m.m_mask; }
5653 private uint m_mask;
5655 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5656 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5657 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5661 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5662 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5664 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5665 /// that that EventListener has activate) and a Single EventSource may also have many
5666 /// event Dispatchers (one for every EventListener that has activated it).
5668 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5669 /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
5670 /// associated with.
5672 internal class EventDispatcher
5674 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
5677 m_EventEnabled = eventEnabled;
5678 m_Listener = listener;
5682 readonly internal EventListener m_Listener; // The dispatcher this entry is for
5683 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
5684 #if FEATURE_ACTIVITYSAMPLING
5685 internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
5686 #endif // FEATURE_ACTIVITYSAMPLING
5688 // Only guarenteed to exist after a InsureInit()
5689 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5690 // Of all listeners for that eventSource.
5694 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5698 public enum EventManifestOptions
5701 /// Only the resources associated with current UI culture are included in the manifest
5705 /// Throw exceptions for any inconsistency encountered
5709 /// Generate a "resources" node under "localization" for every satellite assembly provided
5713 /// Generate the manifest only if the event source needs to be registered on the machine,
5714 /// otherwise return null (but still perform validation if Strict is specified)
5716 OnlyIfNeededForRegistration = 0x4,
5718 /// When generating the manifest do *not* enforce the rule that the current EventSource class
5719 /// must be the base class for the user-defined type passed in. This allows validation of .net
5720 /// event sources using the new validation code
5722 AllowEventSourceOverride = 0x8,
5726 /// ManifestBuilder is designed to isolate the details of the message of the event from the
5727 /// rest of EventSource. This one happens to create XML.
5729 internal class ManifestBuilder
5732 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
5733 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
5735 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
5736 EventManifestOptions flags)
5738 #if FEATURE_MANAGED_ETW_CHANNELS
5739 this.providerName = providerName;
5743 this.resources = resources;
5744 sb = new StringBuilder();
5745 events = new StringBuilder();
5746 templates = new StringBuilder();
5747 opcodeTab = new Dictionary<int, string>();
5748 stringTab = new Dictionary<string, string>();
5749 errors = new List<string>();
5750 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
5752 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5753 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\">");
5754 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5755 sb.Append("<provider name=\"").Append(providerName).
5756 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
5757 if (dllName != null)
5758 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
5760 var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
5761 sb.Append("\" symbol=\"").Append(symbolsName);
5762 sb.Append("\">").AppendLine();
5765 public void AddOpcode(string name, int value)
5767 if ((flags & EventManifestOptions.Strict) != 0)
5769 if (value <= 10 || value >= 239)
5770 ManifestError(Environment.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
5772 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5773 ManifestError(Environment.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
5775 opcodeTab[value] = name;
5777 public void AddTask(string name, int value)
5779 if ((flags & EventManifestOptions.Strict) != 0)
5781 if (value <= 0 || value >= 65535)
5782 ManifestError(Environment.GetResourceString("EventSource_IllegalTaskValue", name, value));
5784 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5785 ManifestError(Environment.GetResourceString("EventSource_TaskCollision", name, prevName, value));
5787 if (taskTab == null)
5788 taskTab = new Dictionary<int, string>();
5789 taskTab[value] = name;
5791 public void AddKeyword(string name, ulong value)
5793 if ((value & (value - 1)) != 0) // Is it a power of 2?
5794 ManifestError(Environment.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
5795 if ((flags & EventManifestOptions.Strict) != 0)
5797 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
5798 ManifestError(Environment.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5800 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5801 ManifestError(Environment.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5803 if (keywordTab == null)
5804 keywordTab = new Dictionary<ulong, string>();
5805 keywordTab[value] = name;
5808 #if FEATURE_MANAGED_ETW_CHANNELS
5810 /// Add a channel. channelAttribute can be null
5812 public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
5814 EventChannel chValue = (EventChannel)value;
5815 if (value < (int)EventChannel.Admin || value > 255)
5816 ManifestError(Environment.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
5817 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
5818 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
5820 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
5821 // but we want to allow them to override the default ones...
5822 ManifestError(Environment.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
5823 name, ((EventChannel)value).ToString()));
5828 ulong kwd = GetChannelKeyword(chValue);
5830 if (channelTab == null)
5831 channelTab = new Dictionary<int, ChannelInfo>(4);
5832 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
5835 private EventChannelType EventChannelToChannelType(EventChannel channel)
5837 #if !ES_BUILD_STANDALONE
5838 Contract.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
5840 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
5842 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
5844 EventChannelAttribute attrib = new EventChannelAttribute();
5845 attrib.EventChannelType = EventChannelToChannelType(channel);
5846 if (attrib.EventChannelType <= EventChannelType.Operational)
5847 attrib.Enabled = true;
5851 public ulong[] GetChannelData()
5853 if (this.channelTab == null)
5855 return new ulong[0];
5858 // We create an array indexed by the channel id for fast look up.
5859 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
5861 foreach (var item in this.channelTab.Keys)
5869 ulong[] channelMask = new ulong[maxkey + 1];
5870 foreach (var item in this.channelTab)
5872 channelMask[item.Key] = item.Value.Keywords;
5879 public void StartEvent(string eventName, EventAttribute eventAttribute)
5881 Contract.Assert(numParams == 0);
5882 Contract.Assert(this.eventName == null);
5883 this.eventName = eventName;
5885 byteArrArgIndices = null;
5887 events.Append(" <event").
5888 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
5889 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
5890 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
5891 Append(" symbol=\"").Append(eventName).Append("\"");
5893 // at this point we add to the manifest's stringTab a message that is as-of-yet
5894 // "untranslated to manifest convention", b/c we don't have the number or position
5895 // of any byte[] args (which require string format index updates)
5896 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
5898 if (eventAttribute.Keywords != 0)
5899 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
5900 if (eventAttribute.Opcode != 0)
5901 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
5902 if (eventAttribute.Task != 0)
5903 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
5904 #if FEATURE_MANAGED_ETW_CHANNELS
5905 if (eventAttribute.Channel != 0)
5907 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
5912 public void AddEventParameter(Type type, string name)
5915 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
5916 if (type == typeof(byte[]))
5918 // mark this index as "extraneous" (it has no parallel in the managed signature)
5919 // we use these values in TranslateToManifestConvention()
5920 if (byteArrArgIndices == null)
5921 byteArrArgIndices = new List<int>(4);
5922 byteArrArgIndices.Add(numParams);
5924 // add an extra field to the template representing the length of the binary blob
5926 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
5929 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
5932 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
5934 // add "length" attribute to the "blob" field in the template (referencing the field added above)
5935 templates.Append(" length=\"").Append(name).Append("Size\"");
5937 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
5938 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
5940 templates.Append(" map=\"").Append(type.Name).Append("\"");
5941 if (mapsTab == null)
5942 mapsTab = new Dictionary<string, Type>();
5943 if (!mapsTab.ContainsKey(type.Name))
5944 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
5947 templates.Append("/>").AppendLine();
5949 public void EndEvent()
5953 templates.Append(" </template>").AppendLine();
5954 events.Append(" template=\"").Append(eventName).Append("Args\"");
5956 events.Append("/>").AppendLine();
5958 if (byteArrArgIndices != null)
5959 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
5961 // at this point we have all the information we need to translate the C# Message
5962 // to the manifest string we'll put in the stringTab
5964 if (stringTab.TryGetValue("event_" + eventName, out msg))
5966 msg = TranslateToManifestConvention(msg, eventName);
5967 stringTab["event_" + eventName] = msg;
5972 byteArrArgIndices = null;
5975 #if FEATURE_MANAGED_ETW_CHANNELS
5976 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
5977 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
5978 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
5979 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
5980 // to channels by the OS infrastructure).
5981 public ulong GetChannelKeyword(EventChannel channel)
5983 if (channelTab == null)
5985 channelTab = new Dictionary<int, ChannelInfo>(4);
5988 if (channelTab.Count == MaxCountChannels)
5989 ManifestError(Environment.GetResourceString("EventSource_MaxChannelExceeded"));
5991 ulong channelKeyword;
5993 if (!channelTab.TryGetValue((int)channel, out info))
5995 channelKeyword = nextChannelKeywordBit;
5996 nextChannelKeywordBit >>= 1;
6000 channelKeyword = info.Keywords;
6003 return channelKeyword;
6007 public byte[] CreateManifest()
6009 string str = CreateManifestString();
6010 return Encoding.UTF8.GetBytes(str);
6013 public IList<string> Errors { get { return errors; } }
6016 /// When validating an event source it adds the error to the error collection.
6017 /// When not validating it throws an exception if runtimeCritical is "true".
6018 /// Otherwise the error is ignored.
6020 /// <param name="msg"></param>
6021 /// <param name="runtimeCritical"></param>
6022 public void ManifestError(string msg, bool runtimeCritical = false)
6024 if ((flags & EventManifestOptions.Strict) != 0)
6026 else if (runtimeCritical)
6027 throw new ArgumentException(msg);
6030 private string CreateManifestString()
6033 #if FEATURE_MANAGED_ETW_CHANNELS
6034 // Write out the channels
6035 if (channelTab != null)
6037 sb.Append(" <channels>").AppendLine();
6038 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
6039 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
6040 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
6041 foreach (var kvpair in sortedChannels)
6043 int channel = kvpair.Key;
6044 ChannelInfo channelInfo = kvpair.Value;
6046 string channelType = null;
6047 string elementName = "channel";
6048 bool enabled = false;
6049 string fullName = null;
6050 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6051 string isolation = null;
6052 string access = null;
6054 if (channelInfo.Attribs != null)
6056 var attribs = channelInfo.Attribs;
6057 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
6058 channelType = attribs.EventChannelType.ToString();
6059 enabled = attribs.Enabled;
6060 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6061 if (attribs.ImportChannel != null)
6063 fullName = attribs.ImportChannel;
6064 elementName = "importChannel";
6066 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
6067 isolation = attribs.Isolation.ToString();
6068 access = attribs.Access;
6071 if (fullName == null)
6072 fullName = providerName + "/" + channelInfo.Name;
6074 sb.Append(" <").Append(elementName);
6075 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
6076 sb.Append(" name=\"").Append(fullName).Append("\"");
6077 if (elementName == "channel") // not applicable to importChannels.
6079 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
6080 sb.Append(" value=\"").Append(channel).Append("\"");
6081 if (channelType != null)
6082 sb.Append(" type=\"").Append(channelType).Append("\"");
6083 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
6084 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6086 sb.Append(" access=\"").Append(access).Append("\"");
6087 if (isolation != null)
6088 sb.Append(" isolation=\"").Append(isolation).Append("\"");
6091 sb.Append("/>").AppendLine();
6093 sb.Append(" </channels>").AppendLine();
6097 // Write out the tasks
6098 if (taskTab != null)
6101 sb.Append(" <tasks>").AppendLine();
6102 var sortedTasks = new List<int>(taskTab.Keys);
6104 foreach (int task in sortedTasks)
6106 sb.Append(" <task");
6107 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
6108 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
6110 sb.Append(" </tasks>").AppendLine();
6113 // Write out the maps
6114 if (mapsTab != null)
6116 sb.Append(" <maps>").AppendLine();
6117 foreach (Type enumType in mapsTab.Values)
6119 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
6120 string mapKind = isbitmap ? "bitMap" : "valueMap";
6121 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
6123 // write out each enum value
6124 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
6125 foreach (FieldInfo staticField in staticFields)
6127 object constantValObj = staticField.GetRawConstantValue();
6128 if (constantValObj != null)
6131 if (constantValObj is int)
6132 hexValue = ((int)constantValObj);
6133 else if (constantValObj is long)
6134 hexValue = ((long)constantValObj);
6138 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
6140 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
6143 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
6144 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
6145 sb.Append("/>").AppendLine();
6148 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
6150 sb.Append(" </maps>").AppendLine();
6153 // Write out the opcodes
6154 sb.Append(" <opcodes>").AppendLine();
6155 var sortedOpcodes = new List<int>(opcodeTab.Keys);
6156 sortedOpcodes.Sort();
6157 foreach (int opcode in sortedOpcodes)
6159 sb.Append(" <opcode");
6160 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
6161 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
6163 sb.Append(" </opcodes>").AppendLine();
6165 // Write out the keywords
6166 if (keywordTab != null)
6168 sb.Append(" <keywords>").AppendLine();
6169 var sortedKeywords = new List<ulong>(keywordTab.Keys);
6170 sortedKeywords.Sort();
6171 foreach (ulong keyword in sortedKeywords)
6173 sb.Append(" <keyword");
6174 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
6175 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
6177 sb.Append(" </keywords>").AppendLine();
6180 sb.Append(" <events>").AppendLine();
6182 sb.Append(" </events>").AppendLine();
6184 sb.Append(" <templates>").AppendLine();
6185 if (templates.Length > 0)
6187 sb.Append(templates);
6191 // Work around a cornercase ETW issue where a manifest with no templates causes
6192 // ETW events to not get sent to their associated channel.
6193 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
6195 sb.Append(" </templates>").AppendLine();
6197 sb.Append("</provider>").AppendLine();
6198 sb.Append("</events>").AppendLine();
6199 sb.Append("</instrumentation>").AppendLine();
6201 // Output the localization information.
6202 sb.Append("<localization>").AppendLine();
6204 List<CultureInfo> cultures = null;
6205 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
6207 cultures = GetSupportedCultures(resources);
6211 cultures = new List<CultureInfo>();
6212 cultures.Add(CultureInfo.CurrentUICulture);
6214 #if ES_BUILD_STANDALONE
6215 var sortedStrings = new List<string>(stringTab.Keys);
6216 sortedStrings.Sort();
6219 var sortedStrings = new string[stringTab.Keys.Count];
6220 stringTab.Keys.CopyTo(sortedStrings, 0);
6221 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
6222 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
6223 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
6224 // access BinaryCompatibility.
6225 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer<string>.Default);
6227 foreach (var ci in cultures)
6229 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
6230 sb.Append(" <stringTable>").AppendLine();
6232 foreach (var stringKey in sortedStrings)
6234 string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
6235 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
6237 sb.Append(" </stringTable>").AppendLine();
6238 sb.Append(" </resources>").AppendLine();
6240 sb.Append("</localization>").AppendLine();
6241 sb.AppendLine("</instrumentationManifest>");
6242 return sb.ToString();
6246 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
6248 stringBuilder.Append(" name=\"").Append(name).Append("\"");
6249 WriteMessageAttrib(sb, elementName, name, name);
6251 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
6253 string key = elementName + "_" + name;
6254 // See if the user wants things localized.
6255 if (resources != null)
6257 // resource fallback: strings in the neutral culture will take precedence over inline strings
6258 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
6259 if (localizedString != null)
6260 value = localizedString;
6265 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
6267 if (stringTab.TryGetValue(key, out prevValue))
6268 ManifestError(Environment.GetResourceString("EventSource_DuplicateStringKey", key), true);
6270 stringTab.Add(key, value);
6272 internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
6274 string value = null;
6275 if (resources != null)
6277 string localizedString = resources.GetString(key, ci);
6278 if (localizedString != null)
6280 value = localizedString;
6281 if (etwFormat && key.StartsWith("event_"))
6283 var evtName = key.Substring("event_".Length);
6284 value = TranslateToManifestConvention(value, evtName);
6288 if (etwFormat && value == null)
6289 stringTab.TryGetValue(key, out value);
6295 /// There's no API to enumerate all languages an assembly is localized into, so instead
6296 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
6299 /// <param name="resources"></param>
6300 /// <returns></returns>
6301 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
6303 var cultures = new List<CultureInfo>();
6304 #if !ES_BUILD_PCL && !FEATURE_CORECLR
6305 foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures /*| CultureTypes.NeutralCultures*/))
6307 if (resources.GetResourceSet(ci, true, false) != null)
6310 #endif // !ES_BUILD_PCL && !FEATURE_CORECLR
6311 if (!cultures.Contains(CultureInfo.CurrentUICulture))
6312 cultures.Insert(0, CultureInfo.CurrentUICulture);
6316 private static string GetLevelName(EventLevel level)
6318 return (((int)level >= 16) ? "" : "win:") + level.ToString();
6321 #if FEATURE_MANAGED_ETW_CHANNELS
6322 private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
6324 ChannelInfo info = null;
6325 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6327 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6328 ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6330 // allow channels to be auto-defined. The well known ones get their well known names, and the
6331 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
6332 if (channelTab == null)
6333 channelTab = new Dictionary<int, ChannelInfo>(4);
6335 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
6336 if (EventChannel.Debug < channel)
6337 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
6339 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6340 if (!channelTab.TryGetValue((int)channel, out info))
6341 ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6343 // events that specify admin channels *must* have non-null "Message" attributes
6344 if (resources != null && eventMessage == null)
6345 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6346 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6347 ManifestError(Environment.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
6351 private string GetTaskName(EventTask task, string eventName)
6353 if (task == EventTask.None)
6357 if (taskTab == null)
6358 taskTab = new Dictionary<int, string>();
6359 if (!taskTab.TryGetValue((int)task, out ret))
6360 ret = taskTab[(int)task] = eventName;
6363 private string GetOpcodeName(EventOpcode opcode, string eventName)
6367 case EventOpcode.Info:
6369 case EventOpcode.Start:
6371 case EventOpcode.Stop:
6373 case EventOpcode.DataCollectionStart:
6374 return "win:DC_Start";
6375 case EventOpcode.DataCollectionStop:
6376 return "win:DC_Stop";
6377 case EventOpcode.Extension:
6378 return "win:Extension";
6379 case EventOpcode.Reply:
6381 case EventOpcode.Resume:
6382 return "win:Resume";
6383 case EventOpcode.Suspend:
6384 return "win:Suspend";
6385 case EventOpcode.Send:
6387 case EventOpcode.Receive:
6388 return "win:Receive";
6392 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6394 ManifestError(Environment.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
6399 private string GetKeywords(ulong keywords, string eventName)
6402 for (ulong bit = 1; bit != 0; bit <<= 1)
6404 if ((keywords & bit) != 0)
6406 string keyword = null;
6407 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6408 (bit >= (ulong)0x1000000000000))
6410 // do not report Windows reserved keywords in the manifest (this allows the code
6411 // to be resilient to potential renaming of these keywords)
6412 keyword = string.Empty;
6414 if (keyword == null)
6416 ManifestError(Environment.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6417 keyword = string.Empty;
6419 if (ret.Length != 0 && keyword.Length != 0)
6421 ret = ret + keyword;
6426 private string GetTypeName(Type type)
6430 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6431 var typeName = GetTypeName(fields[0].FieldType);
6432 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6434 switch (type.GetTypeCode())
6436 case TypeCode.Boolean:
6437 return "win:Boolean";
6441 case TypeCode.UInt16:
6442 return "win:UInt16";
6443 case TypeCode.UInt32:
6444 return "win:UInt32";
6445 case TypeCode.UInt64:
6446 return "win:UInt64";
6447 case TypeCode.SByte:
6449 case TypeCode.Int16:
6451 case TypeCode.Int32:
6453 case TypeCode.Int64:
6455 case TypeCode.String:
6456 return "win:UnicodeString";
6457 case TypeCode.Single:
6459 case TypeCode.Double:
6460 return "win:Double";
6461 case TypeCode.DateTime:
6462 return "win:FILETIME";
6464 if (type == typeof(Guid))
6466 else if (type == typeof(IntPtr))
6467 return "win:Pointer";
6468 else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6469 return "win:Binary";
6470 ManifestError(Environment.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true);
6471 return string.Empty;
6475 private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
6477 if (stringBuilder == null)
6478 stringBuilder = new StringBuilder();
6479 stringBuilder.Append(eventMessage, startIndex, count);
6482 // Manifest messages use %N conventions for their message substitutions. Translate from
6483 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6484 private string TranslateToManifestConvention(string eventMessage, string evtName)
6486 StringBuilder stringBuilder = null; // We lazily create this
6487 int writtenSoFar = 0;
6491 if (i >= eventMessage.Length)
6493 if (stringBuilder == null)
6494 return eventMessage;
6495 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6496 return stringBuilder.ToString();
6499 if (eventMessage[i] == '%')
6501 // handle format message escaping character '%' by escaping it
6502 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6503 stringBuilder.Append("%%");
6507 else if (i < eventMessage.Length - 1 &&
6508 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6510 // handle C# escaped '{" and '}'
6511 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6512 stringBuilder.Append(eventMessage[i]);
6516 else if (eventMessage[i] == '{')
6518 int leftBracket = i;
6521 while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
6523 argNum = argNum * 10 + eventMessage[i] - '0';
6526 if (i < eventMessage.Length && eventMessage[i] == '}')
6529 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6530 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6531 stringBuilder.Append('%').Append(manIndex);
6532 // An '!' after the insert specifier {n} will be interpreted as a literal.
6533 // We'll escape it so that mc.exe does not attempt to consider it the
6534 // beginning of a format string.
6535 if (i < eventMessage.Length && eventMessage[i] == '!')
6538 stringBuilder.Append("%!");
6544 ManifestError(Environment.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
6547 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6549 string[] escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" };
6550 var update = new Action<char, string>(
6553 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6555 stringBuilder.Append(escape);
6558 update(eventMessage[i], escapes[chIdx]);
6565 private int TranslateIndexToManifestConvention(int idx, string evtName)
6567 List<int> byteArrArgIndices;
6568 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6570 foreach (var byArrIdx in byteArrArgIndices)
6572 if (idx >= byArrIdx)
6581 #if FEATURE_MANAGED_ETW_CHANNELS
6585 public ulong Keywords;
6586 public EventChannelAttribute Attribs;
6590 Dictionary<int, string> opcodeTab;
6591 Dictionary<int, string> taskTab;
6592 #if FEATURE_MANAGED_ETW_CHANNELS
6593 Dictionary<int, ChannelInfo> channelTab;
6595 Dictionary<ulong, string> keywordTab;
6596 Dictionary<string, Type> mapsTab;
6598 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6600 #if FEATURE_MANAGED_ETW_CHANNELS
6601 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition
6602 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6605 StringBuilder sb; // Holds the provider information.
6606 StringBuilder events; // Holds the events.
6607 StringBuilder templates;
6609 #if FEATURE_MANAGED_ETW_CHANNELS
6610 string providerName;
6612 ResourceManager resources; // Look up localized strings here.
6613 EventManifestOptions flags;
6614 IList<string> errors; // list of currently encountered errors
6615 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6617 // State we track between StartEvent and EndEvent.
6618 string eventName; // Name of the event currently being processed.
6619 int numParams; // keeps track of the number of args the event has.
6620 List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
6625 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6627 internal struct ManifestEnvelope
6629 public const int MaxChunkSize = 0xFF00;
6630 public enum ManifestFormats : byte
6632 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6635 public ManifestFormats Format;
6636 public byte MajorVersion;
6637 public byte MinorVersion;
6639 public ushort TotalChunks;
6640 public ushort ChunkNumber;