1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
6 // It is available from http://www.codeplex.com/hyperAddin
7 #if ES_BUILD_STANDALONE
8 #define FEATURE_MANAGED_ETW_CHANNELS
9 // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
12 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
14 // Over the years EventSource has become more complex and so it is important to understand
15 // the basic structure of the code to insure that it does not grow more complex.
19 // PRINCIPLE: EventSource - ETW decoupling
21 // Conceptually and EventSouce is something takes event logging data from the source methods
22 // To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
23 // KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListener Which
24 // we will call the EtwEventListener, that forwards commands from ETW to EventSources and
25 // listeners to the EventSources and forwards on those events to ETW. THus the model should
26 // be that you DON'T NEED ETW.
28 // Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
29 // to it directly, but this can be VIEWED AS AN OPTIMIZATION.
31 // Basic Event Data Flow:
33 // There are two ways for event Data to enter the system
34 // 1) WriteEvent* and friends. This is called the 'contract' based approach because
35 // you write a method per event which forms a contract that is know at compile time.
36 // In this scheme each event is given an EVENTID (small integer). which is its identity
37 // 2) Write<T> methods. This is called the 'dynamic' approach because new events
38 // can be created on the fly. Event identity is determined by the event NAME, and these
39 // are not quite as efficient at runtime since you have at least a hash table lookup
40 // on every event write.
42 // EventSource-EventListener transfer fully support both ways of writing events (either contract
43 // based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
44 // types. It is suggested, however, that you use the contract based approach when the event scheme
45 // is known at compile time (that is whenever possible). It is more efficient, but more importantly
46 // it makes the contract very explicit, and centralizes all policy about logging. These are good
47 // things. The Write<T> API is really meant for more ad-hoc
51 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
52 // during the transfer. In particular object identity is not preserved, some objects are morphed,
53 // and not all data types are supported. In particular you can pass
55 // A Valid type to log to an EventSource include
56 // * Primitive data types
57 // * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
58 // * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
60 // This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
62 // Explicitly allowed structs include (* New for V4.6)
63 // * Marked with the EventData attribute
64 // * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
65 // * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
67 // When classes are returned in an EventListener, what is returned is something that implements
68 // IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
69 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
70 // are obvious NOT the original objects.
72 // ETWserialization formats:
74 // As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
75 // copy/morph of that data as described above. In addition the .NET framework supports a conceptual
76 // ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
77 // to be serialized in a way that ETW supports. ETW supports the following serialization formats
79 // 1) Manifest Based serialization.
80 // 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
82 // A key factor is that the Write<T> method, which support on the fly definition of events, can't
83 // support the manifest based serialization because the manifest needs the schema of all events
84 // to be known before any events are emitted. This implies the following
86 // If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
87 // If you use the EventSource(string) constructor for an eventSource (in which you don't
88 // create a subclass), the default is also to use Self-Describing serialization. In addition
89 // you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
90 // Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
92 // Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
94 // *************************************************************************************
95 // *** INTERNALS: Event Propagation
97 // Data enters the system either though
99 // 1) A user defined method in the user defined subclass of EventSource which calls
100 // A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
101 // * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
102 // B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
103 // C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
105 // All event data eventually flows to one of
106 // * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
107 // * WriteEventVarargs(ID, Guid*, object[])
109 // 2) A call to one of the overloads of Write<T>. All these overloads end up in
110 // * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
112 // On output there are the following routines
113 // Writing to all listeners that are NOT ETW, we have the following routines
114 // * WriteToAllListeners(ID, Guid*, Guid*, COUNT, EventData*)
115 // * WriteToAllListeners(ID, Guid*, Guid*, object[])
116 // * WriteToAllListeners(NAME, Guid*, Guid*, EventPayload)
118 // EventPayload is the internal type that implements the IDictionary<string, object> interface
119 // The EventListeners will pass back for serialized classes for nested object, but
120 // WriteToAllListeners(NAME, Guid*, Guid*, EventPayload) unpacks this uses the fields as if they
121 // were parameters to a method.
123 // The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
125 // Writing to ETW, Manifest Based
126 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
127 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
128 // Writing to ETW, Self-Describing format
129 // WriteMultiMerge(NAME, Options, Types, EventData*)
130 // WriteMultiMerge(NAME, Options, Types, object[])
131 // WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
134 // All ETW writes eventually call
135 // EventWriteTransfer (native PINVOKE wrapper)
136 // EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
137 // EventProvider.WriteEventRaw - sets last error
138 // EventSource.WriteEventRaw - Does EventSource exception handling logic
141 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
142 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
144 // Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
145 // how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
146 // since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
147 // can call one of these
148 // WriteMetadata - transforms the type T into serialization meta data blob for that type
149 // WriteObjectData - transforms an object of T into serialization meta data blob for that type
150 // GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
151 // The first two are used to serialize something for ETW. The second one is used to transform the object
152 // for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
153 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
155 // It is an important observation that while EventSource does support users directly calling with EventData*
156 // blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
157 // path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
159 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
161 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
162 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
163 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
164 // This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
165 // opportunity to expose this format to EventListeners in the future.
168 using System.Runtime.CompilerServices;
169 using System.Collections.Generic;
170 using System.Collections.ObjectModel;
171 using System.Diagnostics;
172 using System.Diagnostics.CodeAnalysis;
173 using System.Globalization;
174 using System.Reflection;
175 using System.Resources;
176 using System.Security;
177 #if !CORECLR && !ES_BUILD_PN
178 using System.Security.Permissions;
179 #endif // !CORECLR && !ES_BUILD_PN
182 using System.Threading;
183 using Microsoft.Win32;
185 #if ES_BUILD_STANDALONE
186 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
188 using System.Threading.Tasks;
191 using Microsoft.Reflection;
193 #if !ES_BUILD_AGAINST_DOTNET_V35
194 using Contract = System.Diagnostics.Contracts.Contract;
196 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
199 #if CORECLR || ES_BUILD_PN
200 using Internal.Runtime.Augments;
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 #if FEATURE_EVENTSOURCE_XPLAT
248 private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
249 #endif //FEATURE_EVENTSOURCE_XPLAT
252 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
254 public string Name { get { return m_name; } }
256 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
258 public Guid Guid { get { return m_guid; } }
261 /// Returns true if the eventSource has been enabled at all. This is the preferred test
262 /// to be performed before a relatively expensive EventSource operation.
264 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
265 public bool IsEnabled()
267 return m_eventSourceEnabled;
271 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
273 /// Note that the result of this function is only an approximation on whether a particular
274 /// event is active or not. It is only meant to be used as way of avoiding expensive
275 /// computation for logging when logging is not on, therefore it sometimes returns false
276 /// positives (but is always accurate when returning false). EventSources are free to
277 /// have additional filtering.
279 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
280 public bool IsEnabled(EventLevel level, EventKeywords keywords)
282 return IsEnabled(level, keywords, EventChannel.None);
286 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
287 /// if 'keywords' specifies a channel bit for a channel that is enabled.
289 /// Note that the result of this function only an approximation on whether a particular
290 /// event is active or not. It is only meant to be used as way of avoiding expensive
291 /// computation for logging when logging is not on, therefore it sometimes returns false
292 /// positives (but is always accurate when returning false). EventSources are free to
293 /// have additional filtering.
295 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
296 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
298 if (!m_eventSourceEnabled)
301 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
308 /// Returns the settings for the event source instance
310 public EventSourceSettings Settings
312 get { return m_config; }
317 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
318 /// This API allows you to compute this without actually creating an instance of the EventSource.
319 /// It only needs to reflect over the type.
321 public static Guid GetGuid(Type eventSourceType)
323 if (eventSourceType == null)
324 throw new ArgumentNullException(nameof(eventSourceType));
326 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
327 string name = eventSourceType.Name;
330 if (attrib.Guid != null)
333 #if !ES_BUILD_AGAINST_DOTNET_V35
334 if (Guid.TryParse(attrib.Guid, out g))
337 try { return new Guid(attrib.Guid); }
338 catch (Exception) { }
342 if (attrib.Name != null)
348 throw new ArgumentException(SR.Argument_InvalidTypeName, nameof(eventSourceType));
350 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
353 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
354 /// This API allows you to compute this without actually creating an instance of the EventSource.
355 /// It only needs to reflect over the type.
357 public static string GetName(Type eventSourceType)
359 return GetName(eventSourceType, EventManifestOptions.None);
363 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
364 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
365 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
366 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
368 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
369 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
370 /// which it is embedded. This parameter specifies what name will be used</param>
371 /// <returns>The XML data string</returns>
372 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
374 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
377 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
378 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
379 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
380 /// ensures that the entries in the event log will be "optimally" localized.
382 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
383 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
384 /// which it is embedded. This parameter specifies what name will be used</param>
385 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
386 /// this returns null when the eventSourceType does not require explicit registration</param>
387 /// <returns>The XML data string or null</returns>
388 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
390 if (eventSourceType == null)
391 throw new ArgumentNullException(nameof(eventSourceType));
393 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
394 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
397 // EventListener support
399 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
401 /// <returns></returns>
402 public static IEnumerable<EventSource> GetSources()
404 var ret = new List<EventSource>();
405 lock (EventListener.EventListenersLock)
407 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
409 EventSource eventSource = eventSourceRef.Target as EventSource;
410 if (eventSource != null && !eventSource.IsDisposed)
411 ret.Add(eventSource);
418 /// Send a command to a particular EventSource identified by 'eventSource'.
419 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
420 /// callback. What the EventSource does with the command and its arguments are from
421 /// that point EventSource-specific.
423 /// <param name="eventSource">The instance of EventSource to send the command to</param>
424 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
425 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
426 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
428 if (eventSource == null)
429 throw new ArgumentNullException(nameof(eventSource));
431 // User-defined EventCommands should not conflict with the reserved commands.
432 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
434 throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command));
437 eventSource.SendCommand(null, EventProviderType.ETW, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
440 #if !ES_BUILD_STANDALONE
442 /// This property allows EventSource code to appropriately handle as "different"
443 /// activities started on different threads that have not had an activity created on them.
445 internal static Guid InternalCurrentThreadActivityId
449 Guid retval = CurrentThreadActivityId;
450 if (retval == Guid.Empty)
452 retval = FallbackActivityId;
458 internal static Guid FallbackActivityId
462 #pragma warning disable 612, 618
463 int threadID = AppDomain.GetCurrentThreadId();
465 // Managed thread IDs are more aggressively re-used than native thread IDs,
466 // so we'll use the latter...
467 return new Guid(unchecked((uint)threadID),
468 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
469 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
470 #pragma warning restore 612, 618
473 #endif // !ES_BUILD_STANDALONE
475 // Error APIs. (We don't throw by default, but you can probe for status)
479 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
480 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
482 /// The event source constructor does not throw exceptions. Instead we remember any exception that
483 /// was generated (it is also logged to Trace.WriteLine).
485 public Exception ConstructionException { get { return m_constructionException; } }
488 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
489 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
490 /// (e.g. like the built in ETW listener). These traits are specified at EventSource
491 /// construction time and can be retrieved by using this GetTrait API.
493 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
494 /// <returns>The value string associated with key. Will return null if there is no such key.</returns>
495 public string GetTrait(string key)
497 if (m_traits != null)
499 for (int i = 0; i < m_traits.Length - 1; i += 2)
501 if (m_traits[i] == key)
502 return m_traits[i + 1];
509 /// Displays the name and GUID for the eventSource for debugging purposes.
511 public override string ToString()
513 return SR.Format(SR.EventSource_ToString, Name, Guid);
517 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
519 public event EventHandler<EventCommandEventArgs> EventCommandExecuted
523 m_eventCommandExecuted += value;
525 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
526 // It should get a chance to handle the deferred commands.
527 EventCommandEventArgs deferredCommands = m_deferredCommands;
528 while (deferredCommands != null)
530 value(this, deferredCommands);
531 deferredCommands = deferredCommands.nextCommand;
536 m_eventCommandExecuted -= value;
542 /// This is the constructor that most users will use to create their eventSource. It takes
543 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
544 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
545 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
546 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
547 /// the ETW provider name.
549 protected EventSource()
550 : this(EventSourceSettings.EtwManifestEventFormat)
555 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
556 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
557 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
558 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
559 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
560 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
562 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
564 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
565 protected EventSource(bool throwOnEventWriteErrors)
566 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
570 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
572 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
575 /// Construct an EventSource with additional non-default settings.
577 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
578 /// The first string is the key and the second is the value. These are not interpreted by EventSource
579 /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string).
581 /// <param name="settings">See EventSourceSettings for more.</param>
582 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
583 protected EventSource(EventSourceSettings settings, params string[] traits)
585 m_config = ValidateSettings(settings);
587 Guid eventSourceGuid;
588 string eventSourceName;
590 EventMetadata[] eventDescriptors;
592 GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
594 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
596 var myType = this.GetType();
597 eventSourceGuid = GetGuid(myType);
598 eventSourceName = GetName(myType);
601 Initialize(eventSourceGuid, eventSourceName, traits);
604 #if FEATURE_PERFTRACING
605 // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly
606 // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere.
607 private unsafe void DefineEventPipeEvents()
609 // If the EventSource is set to emit all events as TraceLogging events, skip this initialization.
610 // Events will be defined when they are emitted for the first time.
611 if(SelfDescribingEvents)
616 Debug.Assert(m_eventData != null);
617 Debug.Assert(m_eventPipeProvider != null);
618 int cnt = m_eventData.Length;
619 for (int i = 0; i < cnt; i++)
621 uint eventID = (uint)m_eventData[i].Descriptor.EventId;
625 byte[] metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(m_eventData[i]);
626 uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
628 string eventName = m_eventData[i].Name;
629 long keywords = m_eventData[i].Descriptor.Keywords;
630 uint eventVersion = m_eventData[i].Descriptor.Version;
631 uint level = m_eventData[i].Descriptor.Level;
633 fixed (byte *pMetadata = metadata)
635 IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(
644 Debug.Assert(eventHandle != IntPtr.Zero);
645 m_eventData[i].EventHandle = eventHandle;
651 internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
654 // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
655 // On other architectures it is a no-op.
657 // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
658 // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
660 // This will be implemented by an IL rewriter, so we can't make this method abstract or the initial build of the subclass would fail.
662 eventSourceGuid = Guid.Empty;
663 eventSourceName = null;
665 manifestBytes = null;
671 /// This method is called when the eventSource is updated by the controller.
673 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
675 #pragma warning disable 1591
676 // optimized for common signatures (no args)
677 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
678 protected unsafe void WriteEvent(int eventId)
680 WriteEventCore(eventId, 0, null);
683 // optimized for common signatures (ints)
684 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
685 protected unsafe void WriteEvent(int eventId, int arg1)
687 if (m_eventSourceEnabled)
689 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
690 descrs[0].DataPointer = (IntPtr)(&arg1);
692 descrs[0].Reserved = 0;
693 WriteEventCore(eventId, 1, descrs);
697 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
698 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
700 if (m_eventSourceEnabled)
702 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
703 descrs[0].DataPointer = (IntPtr)(&arg1);
705 descrs[0].Reserved = 0;
706 descrs[1].DataPointer = (IntPtr)(&arg2);
708 descrs[1].Reserved = 0;
709 WriteEventCore(eventId, 2, descrs);
713 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
714 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
716 if (m_eventSourceEnabled)
718 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
719 descrs[0].DataPointer = (IntPtr)(&arg1);
721 descrs[0].Reserved = 0;
722 descrs[1].DataPointer = (IntPtr)(&arg2);
724 descrs[1].Reserved = 0;
725 descrs[2].DataPointer = (IntPtr)(&arg3);
727 descrs[2].Reserved = 0;
728 WriteEventCore(eventId, 3, descrs);
732 // optimized for common signatures (longs)
733 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
734 protected unsafe void WriteEvent(int eventId, long arg1)
736 if (m_eventSourceEnabled)
738 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
739 descrs[0].DataPointer = (IntPtr)(&arg1);
741 descrs[0].Reserved = 0;
742 WriteEventCore(eventId, 1, descrs);
746 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
747 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
749 if (m_eventSourceEnabled)
751 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
752 descrs[0].DataPointer = (IntPtr)(&arg1);
754 descrs[0].Reserved = 0;
755 descrs[1].DataPointer = (IntPtr)(&arg2);
757 descrs[1].Reserved = 0;
758 WriteEventCore(eventId, 2, descrs);
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, long arg1, long arg2, long arg3)
765 if (m_eventSourceEnabled)
767 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
768 descrs[0].DataPointer = (IntPtr)(&arg1);
770 descrs[0].Reserved = 0;
771 descrs[1].DataPointer = (IntPtr)(&arg2);
773 descrs[1].Reserved = 0;
774 descrs[2].DataPointer = (IntPtr)(&arg3);
776 descrs[2].Reserved = 0;
777 WriteEventCore(eventId, 3, descrs);
781 // optimized for common signatures (strings)
782 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
783 protected unsafe void WriteEvent(int eventId, string arg1)
785 if (m_eventSourceEnabled)
787 if (arg1 == null) arg1 = "";
788 fixed (char* string1Bytes = arg1)
790 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
791 descrs[0].DataPointer = (IntPtr)string1Bytes;
792 descrs[0].Size = ((arg1.Length + 1) * 2);
793 descrs[0].Reserved = 0;
794 WriteEventCore(eventId, 1, descrs);
799 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
800 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
802 if (m_eventSourceEnabled)
804 if (arg1 == null) arg1 = "";
805 if (arg2 == null) arg2 = "";
806 fixed (char* string1Bytes = arg1)
807 fixed (char* string2Bytes = arg2)
809 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
810 descrs[0].DataPointer = (IntPtr)string1Bytes;
811 descrs[0].Size = ((arg1.Length + 1) * 2);
812 descrs[0].Reserved = 0;
813 descrs[1].DataPointer = (IntPtr)string2Bytes;
814 descrs[1].Size = ((arg2.Length + 1) * 2);
815 descrs[1].Reserved = 0;
816 WriteEventCore(eventId, 2, descrs);
821 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
822 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
824 if (m_eventSourceEnabled)
826 if (arg1 == null) arg1 = "";
827 if (arg2 == null) arg2 = "";
828 if (arg3 == null) arg3 = "";
829 fixed (char* string1Bytes = arg1)
830 fixed (char* string2Bytes = arg2)
831 fixed (char* string3Bytes = arg3)
833 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
834 descrs[0].DataPointer = (IntPtr)string1Bytes;
835 descrs[0].Size = ((arg1.Length + 1) * 2);
836 descrs[0].Reserved = 0;
837 descrs[1].DataPointer = (IntPtr)string2Bytes;
838 descrs[1].Size = ((arg2.Length + 1) * 2);
839 descrs[1].Reserved = 0;
840 descrs[2].DataPointer = (IntPtr)string3Bytes;
841 descrs[2].Size = ((arg3.Length + 1) * 2);
842 descrs[2].Reserved = 0;
843 WriteEventCore(eventId, 3, descrs);
848 // optimized for common signatures (string and ints)
849 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
850 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
852 if (m_eventSourceEnabled)
854 if (arg1 == null) arg1 = "";
855 fixed (char* string1Bytes = arg1)
857 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
858 descrs[0].DataPointer = (IntPtr)string1Bytes;
859 descrs[0].Size = ((arg1.Length + 1) * 2);
860 descrs[0].Reserved = 0;
861 descrs[1].DataPointer = (IntPtr)(&arg2);
863 descrs[1].Reserved = 0;
864 WriteEventCore(eventId, 2, descrs);
869 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
870 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
872 if (m_eventSourceEnabled)
874 if (arg1 == null) arg1 = "";
875 fixed (char* string1Bytes = arg1)
877 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
878 descrs[0].DataPointer = (IntPtr)string1Bytes;
879 descrs[0].Size = ((arg1.Length + 1) * 2);
880 descrs[0].Reserved = 0;
881 descrs[1].DataPointer = (IntPtr)(&arg2);
883 descrs[1].Reserved = 0;
884 descrs[2].DataPointer = (IntPtr)(&arg3);
886 descrs[2].Reserved = 0;
887 WriteEventCore(eventId, 3, descrs);
892 // optimized for common signatures (string and longs)
893 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
894 protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
896 if (m_eventSourceEnabled)
898 if (arg1 == null) arg1 = "";
899 fixed (char* string1Bytes = arg1)
901 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
902 descrs[0].DataPointer = (IntPtr)string1Bytes;
903 descrs[0].Size = ((arg1.Length + 1) * 2);
904 descrs[0].Reserved = 0;
905 descrs[1].DataPointer = (IntPtr)(&arg2);
907 descrs[1].Reserved = 0;
908 WriteEventCore(eventId, 2, descrs);
913 // optimized for common signatures (long and string)
914 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
915 protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
917 if (m_eventSourceEnabled)
919 if (arg2 == null) arg2 = "";
920 fixed (char* string2Bytes = arg2)
922 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
923 descrs[0].DataPointer = (IntPtr)(&arg1);
925 descrs[0].Reserved = 0;
926 descrs[1].DataPointer = (IntPtr)string2Bytes;
927 descrs[1].Size = ((arg2.Length + 1) * 2);
928 descrs[1].Reserved = 0;
929 WriteEventCore(eventId, 2, descrs);
934 // optimized for common signatures (int and string)
935 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
936 protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
938 if (m_eventSourceEnabled)
940 if (arg2 == null) arg2 = "";
941 fixed (char* string2Bytes = arg2)
943 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
944 descrs[0].DataPointer = (IntPtr)(&arg1);
946 descrs[0].Reserved = 0;
947 descrs[1].DataPointer = (IntPtr)string2Bytes;
948 descrs[1].Size = ((arg2.Length + 1) * 2);
949 descrs[1].Reserved = 0;
950 WriteEventCore(eventId, 2, descrs);
955 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
956 protected unsafe void WriteEvent(int eventId, byte[] arg1)
958 if (m_eventSourceEnabled)
960 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
961 if (arg1 == null || arg1.Length == 0)
964 descrs[0].DataPointer = (IntPtr)(&blobSize);
966 descrs[0].Reserved = 0;
967 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
969 descrs[1].Reserved = 0;
970 WriteEventCore(eventId, 2, descrs);
974 int blobSize = arg1.Length;
975 fixed (byte* blob = &arg1[0])
977 descrs[0].DataPointer = (IntPtr)(&blobSize);
979 descrs[0].Reserved = 0;
980 descrs[1].DataPointer = (IntPtr)blob;
981 descrs[1].Size = blobSize;
982 descrs[1].Reserved = 0;
983 WriteEventCore(eventId, 2, descrs);
989 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
990 protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
992 if (m_eventSourceEnabled)
994 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
995 descrs[0].DataPointer = (IntPtr)(&arg1);
997 descrs[0].Reserved = 0;
998 if (arg2 == null || arg2.Length == 0)
1001 descrs[1].DataPointer = (IntPtr)(&blobSize);
1003 descrs[1].Reserved = 0;
1004 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1006 descrs[2].Reserved = 0;
1007 WriteEventCore(eventId, 3, descrs);
1011 int blobSize = arg2.Length;
1012 fixed (byte* blob = &arg2[0])
1014 descrs[1].DataPointer = (IntPtr)(&blobSize);
1016 descrs[1].Reserved = 0;
1017 descrs[2].DataPointer = (IntPtr)blob;
1018 descrs[2].Size = blobSize;
1019 descrs[2].Reserved = 0;
1020 WriteEventCore(eventId, 3, descrs);
1026 #pragma warning restore 1591
1029 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1031 protected internal struct EventData
1034 /// Address where the one argument lives (if this points to managed memory you must ensure the
1035 /// managed object is pinned.
1037 public unsafe IntPtr DataPointer { get { return (IntPtr)(void*)m_Ptr; } set { m_Ptr = unchecked((ulong)(void*)value); } }
1040 /// Size of the argument referenced by DataPointer
1042 public int Size { get { return m_Size; } set { m_Size = value; } }
1045 /// Reserved by ETW. This property is present to ensure that we can zero it
1046 /// since System.Private.CoreLib uses are not zero'd.
1048 internal int Reserved { get { return m_Reserved; } set { m_Reserved = value; } }
1052 /// Initializes the members of this EventData object to point at a previously-pinned
1053 /// tracelogging-compatible metadata blob.
1055 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1056 /// <param name="size">The size of the metadata blob.</param>
1057 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1058 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1060 this.m_Ptr = (ulong)pointer;
1062 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1065 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1066 // the way EventWrite wants it.
1067 internal ulong m_Ptr;
1068 internal int m_Size;
1069 #pragma warning disable 0649
1070 internal int m_Reserved; // Used to pad the size to match the Win32 API
1071 #pragma warning restore 0649
1076 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1077 /// do this, while straightforward, is unsafe.
1081 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1083 /// if (IsEnabled())
1085 /// if (arg2 == null) arg2 = "";
1086 /// fixed (char* string2Bytes = arg2)
1088 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1089 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1090 /// descrs[0].Size = 8;
1091 /// descrs[0].Reserved = 0;
1092 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1093 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1094 /// descrs[1].Reserved = 0;
1095 /// WriteEventCore(eventId, 2, descrs);
1101 [CLSCompliant(false)]
1102 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1104 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1108 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1109 /// that you use to do this, while straightforward, is unsafe. The only difference from
1110 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1114 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1116 /// if (IsEnabled())
1118 /// if (arg2 == null) arg2 = "";
1119 /// fixed (char* string2Bytes = arg2)
1121 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1122 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1123 /// descrs[0].Size = 8;
1124 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1125 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1126 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1132 [CLSCompliant(false)]
1133 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1135 if (m_eventSourceEnabled)
1139 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1140 if (relatedActivityId != null)
1141 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1143 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1144 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1145 Guid* pActivityId = null;
1146 Guid activityId = Guid.Empty;
1147 Guid relActivityId = Guid.Empty;
1149 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1150 ((activityOptions & EventActivityOptions.Disable) == 0))
1152 if (opcode == EventOpcode.Start)
1154 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1156 else if (opcode == EventOpcode.Stop)
1158 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1161 if (activityId != Guid.Empty)
1162 pActivityId = &activityId;
1163 if (relActivityId != Guid.Empty)
1164 relatedActivityId = &relActivityId;
1167 #if FEATURE_MANAGED_ETW
1168 if (m_eventData[eventId].EnabledForETW
1169 #if FEATURE_PERFTRACING
1170 || m_eventData[eventId].EnabledForEventPipe
1171 #endif // FEATURE_PERFTRACING
1174 if (!SelfDescribingEvents)
1176 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1177 ThrowEventSourceException(m_eventData[eventId].Name);
1178 #if FEATURE_PERFTRACING
1179 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1180 ThrowEventSourceException(m_eventData[eventId].Name);
1181 #endif // FEATURE_PERFTRACING
1185 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1188 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1189 m_eventData[eventId].Tags,
1190 m_eventData[eventId].Parameters);
1191 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1194 EventSourceOptions opt = new EventSourceOptions
1196 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1197 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1198 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1201 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1204 #endif // FEATURE_MANAGED_ETW
1206 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1207 WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data);
1209 catch (Exception ex)
1211 if (ex is EventSourceException)
1214 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1219 // fallback varags helpers.
1221 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1222 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1223 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1224 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1225 /// check so that the varargs call is not made when the EventSource is not active.
1227 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1228 protected unsafe void WriteEvent(int eventId, params object[] args)
1230 WriteEventVarargs(eventId, null, args);
1234 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1235 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1236 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1237 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1238 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1239 /// check so that the varargs call is not made when the EventSource is not active.
1241 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1243 WriteEventVarargs(eventId, &relatedActivityId, args);
1248 #region IDisposable Members
1250 /// Disposes of an EventSource.
1252 public void Dispose()
1255 GC.SuppressFinalize(this);
1258 /// Disposes of an EventSource.
1261 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1263 /// 1. We may be called more than once: do nothing after the first call.
1264 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1266 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1267 protected virtual void Dispose(bool disposing)
1271 #if FEATURE_MANAGED_ETW
1272 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1273 // even in scenarios with a high volume of ETW events.
1274 if (m_eventSourceEnabled)
1278 SendManifest(m_rawManifest);
1281 { } // If it fails, simply give up.
1282 m_eventSourceEnabled = false;
1284 if (m_etwProvider != null)
1286 m_etwProvider.Dispose();
1287 m_etwProvider = null;
1290 #if FEATURE_PERFTRACING
1291 if (m_eventPipeProvider != null)
1293 m_eventPipeProvider.Dispose();
1294 m_eventPipeProvider = null;
1298 m_eventSourceEnabled = false;
1299 m_eventSourceDisposed = true;
1302 /// Finalizer for EventSource
1306 this.Dispose(false);
1312 private unsafe void WriteEventRaw(
1314 ref EventDescriptor eventDescriptor,
1317 Guid* relatedActivityID,
1321 #if FEATURE_MANAGED_ETW
1322 if (m_etwProvider == null)
1324 ThrowEventSourceException(eventName);
1328 if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1329 ThrowEventSourceException(eventName);
1331 #endif // FEATURE_MANAGED_ETW
1332 #if FEATURE_PERFTRACING
1333 if (m_eventPipeProvider == null)
1335 ThrowEventSourceException(eventName);
1339 if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1340 ThrowEventSourceException(eventName);
1342 #endif // FEATURE_PERFTRACING
1345 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1346 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1347 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1348 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1351 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1352 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1354 m_config = ValidateSettings(settings);
1355 Initialize(eventSourceGuid, eventSourceName, traits);
1359 /// This method is responsible for the common initialization path from our constructors. It must
1360 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1361 /// "Log", such an exception would become a cached exception for the initialization of the static
1362 /// member, and any future access to the "Log" would throw the cached exception).
1364 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1365 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1370 if (m_traits != null && m_traits.Length % 2 != 0)
1372 throw new ArgumentException(SR.EventSource_TraitEven, nameof(traits));
1375 if (eventSourceGuid == Guid.Empty)
1377 throw new ArgumentException(SR.EventSource_NeedGuid);
1380 if (eventSourceName == null)
1382 throw new ArgumentException(SR.EventSource_NeedName);
1385 m_name = eventSourceName;
1386 m_guid = eventSourceGuid;
1388 //Enable Implicit Activity tracker
1389 m_activityTracker = ActivityTracker.Instance;
1391 #if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
1392 // Create and register our provider traits. We do this early because it is needed to log errors
1393 // In the self-describing event case.
1394 this.InitializeProviderMetadata();
1396 #if FEATURE_MANAGED_ETW
1397 // Register the provider with ETW
1398 var etwProvider = new OverideEventProvider(this, EventProviderType.ETW);
1399 etwProvider.Register(this);
1401 #if FEATURE_PERFTRACING
1402 // Register the provider with EventPipe
1403 var eventPipeProvider = new OverideEventProvider(this, EventProviderType.EventPipe);
1404 eventPipeProvider.Register(this);
1406 // Add the eventSource to the global (weak) list.
1407 // This also sets m_id, which is the index in the list.
1408 EventListener.AddEventSource(this);
1410 #if FEATURE_MANAGED_ETW
1411 // OK if we get this far without an exception, then we can at least write out error messages.
1412 // Set m_provider, which allows this.
1413 m_etwProvider = etwProvider;
1415 #if PLATFORM_WINDOWS
1416 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1417 // API available on OS >= Win 8 and patched Win 7.
1418 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1419 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1422 int setInformationResult;
1423 System.Runtime.InteropServices.GCHandle metadataHandle =
1424 System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1425 IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1427 setInformationResult = m_etwProvider.SetInformation(
1428 UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1430 (uint)this.providerMetadata.Length);
1432 metadataHandle.Free();
1434 #endif // PLATFORM_WINDOWS
1435 #endif // FEATURE_MANAGED_ETW
1437 #if FEATURE_PERFTRACING
1438 m_eventPipeProvider = eventPipeProvider;
1440 Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1441 // We are logically completely initialized at this point.
1442 m_completelyInited = true;
1446 if (m_constructionException == null)
1447 m_constructionException = e;
1448 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1451 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1452 lock (EventListener.EventListenersLock)
1454 // If there are any deferred commands, we can do them now.
1455 // This is the most likely place for exceptions to happen.
1456 // Note that we are NOT resetting m_deferredCommands to NULL here,
1457 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1458 EventCommandEventArgs deferredCommands = m_deferredCommands;
1459 while (deferredCommands != null)
1461 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1462 deferredCommands = deferredCommands.nextCommand;
1467 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1469 if (eventSourceType == null)
1470 throw new ArgumentNullException(nameof(eventSourceType));
1472 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1473 if (attrib != null && attrib.Name != null)
1476 return eventSourceType.Name;
1480 /// Implements the SHA1 hashing algorithm. Note that this
1481 /// implementation is for hashing public information. Do not
1482 /// use this code to hash private data, as this implementation does
1483 /// not take any steps to avoid information disclosure.
1485 private struct Sha1ForNonSecretPurposes
1487 private long length; // Total message length in bits
1488 private uint[] w; // Workspace
1489 private int pos; // Length of current chunk in bytes
1492 /// Call Start() to initialize the hash object.
1498 this.w = new uint[85];
1503 this.w[80] = 0x67452301;
1504 this.w[81] = 0xEFCDAB89;
1505 this.w[82] = 0x98BADCFE;
1506 this.w[83] = 0x10325476;
1507 this.w[84] = 0xC3D2E1F0;
1511 /// Adds an input byte to the hash.
1513 /// <param name="input">Data to include in the hash.</param>
1514 public void Append(byte input)
1516 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1517 if (64 == ++this.pos)
1524 /// Adds input bytes to the hash.
1526 /// <param name="input">
1527 /// Data to include in the hash. Must not be null.
1529 public void Append(byte[] input)
1531 foreach (var b in input)
1538 /// Retrieves the hash value.
1539 /// Note that after calling this function, the hash object should
1540 /// be considered uninitialized. Subsequent calls to Append or
1541 /// Finish will produce useless results. Call Start() to
1544 /// <param name="output">
1545 /// Buffer to receive the hash value. Must not be null.
1546 /// Up to 20 bytes of hash will be written to the output buffer.
1547 /// If the buffer is smaller than 20 bytes, the remaining hash
1548 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1549 /// rest of the buffer is left unmodified.
1551 public void Finish(byte[] output)
1553 long l = this.length + 8 * this.pos;
1555 while (this.pos != 56)
1562 this.Append((byte)(l >> 56));
1563 this.Append((byte)(l >> 48));
1564 this.Append((byte)(l >> 40));
1565 this.Append((byte)(l >> 32));
1566 this.Append((byte)(l >> 24));
1567 this.Append((byte)(l >> 16));
1568 this.Append((byte)(l >> 8));
1569 this.Append((byte)l);
1571 int end = output.Length < 20 ? output.Length : 20;
1572 for (int i = 0; i != end; i++)
1574 uint temp = this.w[80 + i / 4];
1575 output[i] = (byte)(temp >> 24);
1576 this.w[80 + i / 4] = temp << 8;
1582 /// Called when this.pos reaches 64.
1584 private void Drain()
1586 for (int i = 16; i != 80; i++)
1588 this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1593 uint a = this.w[80];
1594 uint b = this.w[81];
1595 uint c = this.w[82];
1596 uint d = this.w[83];
1597 uint e = this.w[84];
1599 for (int i = 0; i != 20; i++)
1601 const uint k = 0x5A827999;
1602 uint f = (b & c) | ((~b) & d);
1603 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1606 for (int i = 20; i != 40; i++)
1609 const uint k = 0x6ED9EBA1;
1610 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1613 for (int i = 40; i != 60; i++)
1615 uint f = (b & c) | (b & d) | (c & d);
1616 const uint k = 0x8F1BBCDC;
1617 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1620 for (int i = 60; i != 80; i++)
1623 const uint k = 0xCA62C1D6;
1624 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1634 this.length += 512; // 64 bytes == 512 bits
1638 private static uint Rol1(uint input)
1640 return (input << 1) | (input >> 31);
1643 private static uint Rol5(uint input)
1645 return (input << 5) | (input >> 27);
1648 private static uint Rol30(uint input)
1650 return (input << 30) | (input >> 2);
1654 private static Guid GenerateGuidFromName(string name)
1656 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1657 var hash = new Sha1ForNonSecretPurposes();
1659 hash.Append(namespaceBytes);
1661 Array.Resize(ref bytes, 16);
1664 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1665 return new Guid(bytes);
1668 private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1670 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1671 // the recursion, but can we do this in a robust way?
1673 IntPtr dataPointer = data->DataPointer;
1674 // advance to next EventData in array
1677 Type dataType = GetDataType(m_eventData[eventId], parameterId);
1680 if (dataType == typeof(IntPtr))
1682 return *((IntPtr*)dataPointer);
1684 else if (dataType == typeof(int))
1686 return *((int*)dataPointer);
1688 else if (dataType == typeof(uint))
1690 return *((uint*)dataPointer);
1692 else if (dataType == typeof(long))
1694 return *((long*)dataPointer);
1696 else if (dataType == typeof(ulong))
1698 return *((ulong*)dataPointer);
1700 else if (dataType == typeof(byte))
1702 return *((byte*)dataPointer);
1704 else if (dataType == typeof(sbyte))
1706 return *((sbyte*)dataPointer);
1708 else if (dataType == typeof(short))
1710 return *((short*)dataPointer);
1712 else if (dataType == typeof(ushort))
1714 return *((ushort*)dataPointer);
1716 else if (dataType == typeof(float))
1718 return *((float*)dataPointer);
1720 else if (dataType == typeof(double))
1722 return *((double*)dataPointer);
1724 else if (dataType == typeof(decimal))
1726 return *((decimal*)dataPointer);
1728 else if (dataType == typeof(bool))
1730 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1731 if (*((int*)dataPointer) == 1)
1740 else if (dataType == typeof(Guid))
1742 return *((Guid*)dataPointer);
1744 else if (dataType == typeof(char))
1746 return *((char*)dataPointer);
1748 else if (dataType == typeof(DateTime))
1750 long dateTimeTicks = *((long*)dataPointer);
1751 return DateTime.FromFileTimeUtc(dateTimeTicks);
1753 else if (dataType == typeof(byte[]))
1755 // byte[] are written to EventData* as an int followed by a blob
1756 int cbSize = *((int*)dataPointer);
1757 byte[] blob = new byte[cbSize];
1758 dataPointer = data->DataPointer;
1760 for (int i = 0; i < cbSize; ++i)
1761 blob[i] = *((byte*)(dataPointer + i));
1764 else if (dataType == typeof(byte*))
1766 // TODO: how do we want to handle this? For now we ignore it...
1771 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1778 m_EventSourceInDecodeObject = true;
1780 if (dataType.IsEnum())
1782 dataType = Enum.GetUnderlyingType(dataType);
1787 // Everything else is marshaled as a string.
1788 // ETW strings are NULL-terminated, so marshal everything up to the first
1789 // null in the string.
1790 //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1791 if(dataPointer == IntPtr.Zero)
1796 return new string((char *)dataPointer);
1801 m_EventSourceInDecodeObject = false;
1806 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1808 private EventDispatcher GetDispatcher(EventListener listener)
1810 EventDispatcher dispatcher = m_Dispatchers;
1811 while (dispatcher != null)
1813 if (dispatcher.m_Listener == listener)
1815 dispatcher = dispatcher.m_Next;
1820 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1822 if (m_eventSourceEnabled)
1826 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1827 if (childActivityID != null)
1829 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1831 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1832 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1833 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1834 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1835 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1836 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1837 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1838 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1839 if (!m_eventData[eventId].HasRelatedActivityID)
1841 throw new ArgumentException(SR.EventSource_NoRelatedActivityId);
1845 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1847 Guid* pActivityId = null;
1848 Guid activityId = Guid.Empty;
1849 Guid relatedActivityId = Guid.Empty;
1850 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1851 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1853 if (childActivityID == null &&
1854 ((activityOptions & EventActivityOptions.Disable) == 0))
1856 if (opcode == EventOpcode.Start)
1858 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1860 else if (opcode == EventOpcode.Stop)
1862 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1865 if (activityId != Guid.Empty)
1866 pActivityId = &activityId;
1867 if (relatedActivityId != Guid.Empty)
1868 childActivityID = &relatedActivityId;
1871 #if FEATURE_MANAGED_ETW
1872 if (m_eventData[eventId].EnabledForETW
1873 #if FEATURE_PERFTRACING
1874 || m_eventData[eventId].EnabledForEventPipe
1875 #endif // FEATURE_PERFTRACING
1878 if (!SelfDescribingEvents)
1880 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1881 ThrowEventSourceException(m_eventData[eventId].Name);
1882 #if FEATURE_PERFTRACING
1883 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1884 ThrowEventSourceException(m_eventData[eventId].Name);
1885 #endif // FEATURE_PERFTRACING
1889 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1892 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1894 m_eventData[eventId].Parameters);
1895 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1898 // TODO: activity ID support
1899 EventSourceOptions opt = new EventSourceOptions
1901 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1902 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1903 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1906 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1909 #endif // FEATURE_MANAGED_ETW
1910 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1912 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1913 // Maintain old behavior - object identity is preserved
1914 if (AppContextSwitches.PreserveEventListnerObjectIdentity)
1916 WriteToAllListeners(eventId, pActivityId, childActivityID, args);
1919 #endif // !ES_BUILD_STANDALONE
1921 object[] serializedArgs = SerializeEventArgs(eventId, args);
1922 WriteToAllListeners(eventId, pActivityId, childActivityID, serializedArgs);
1926 catch (Exception ex)
1928 if (ex is EventSourceException)
1931 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1936 private unsafe object[] SerializeEventArgs(int eventId, object[] args)
1938 TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
1939 if (eventTypes == null)
1941 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1943 m_eventData[eventId].Parameters);
1944 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
1946 var eventData = new object[eventTypes.typeInfos.Length];
1947 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
1949 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
1955 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
1956 /// checks that they in fact match and logs a warning to the debugger if they don't.
1958 /// <param name="infos"></param>
1959 /// <param name="args"></param>
1960 private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
1962 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
1963 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
1964 // writing to the debugger log on PCL.
1965 bool typesMatch = args.Length == infos.Length;
1968 while (typesMatch && i < args.Length)
1970 Type pType = infos[i].ParameterType;
1972 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
1973 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
1974 // argument is null and the parameter type is non-nullable.
1975 if ((args[i] != null && (args[i].GetType() != pType))
1976 || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
1988 System.Diagnostics.Debugger.Log(0, null, SR.EventSource_VarArgsParameterMismatch + "\r\n");
1990 #endif //!ES_BUILD_PCL
1993 private unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
1995 // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
1996 // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
1997 // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
1998 // we just used the modifiedParamCount here -- so we need both.
1999 int paramCount = GetParameterCount(m_eventData[eventId]);
2000 int modifiedParamCount = 0;
2001 for (int i = 0; i < paramCount; i++)
2003 Type parameterType = GetDataType(m_eventData[eventId], i);
2004 if (parameterType == typeof(byte[]))
2006 modifiedParamCount += 2;
2010 modifiedParamCount++;
2013 if (eventDataCount != modifiedParamCount)
2015 ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, eventDataCount, paramCount), true);
2016 paramCount = Math.Min(paramCount, eventDataCount);
2019 object[] args = new object[paramCount];
2021 EventSource.EventData* dataPtr = data;
2022 for (int i = 0; i < paramCount; i++)
2023 args[i] = DecodeObject(eventId, i, ref dataPtr);
2024 WriteToAllListeners(eventId, activityID, childActivityID, args);
2027 // helper for writing to all EventListeners attached the current eventSource.
2028 internal unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, params object[] args)
2030 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2031 eventCallbackArgs.EventId = eventId;
2032 if (activityID != null)
2033 eventCallbackArgs.ActivityId = *activityID;
2034 if (childActivityID != null)
2035 eventCallbackArgs.RelatedActivityId = *childActivityID;
2036 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2037 eventCallbackArgs.Message = m_eventData[eventId].Message;
2038 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2040 DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2043 private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2045 Exception lastThrownException = null;
2046 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2048 Debug.Assert(dispatcher.m_EventEnabled != null);
2049 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2054 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2058 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2059 + e.Message, false);
2060 lastThrownException = e;
2066 if (lastThrownException != null)
2068 throw new EventSourceException(lastThrownException);
2072 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2073 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2075 #if FEATURE_MANAGED_ETW
2076 if (m_etwProvider != null)
2078 string eventName = "EventSourceMessage";
2079 if (SelfDescribingEvents)
2081 EventSourceOptions opt = new EventSourceOptions
2083 Keywords = (EventKeywords)unchecked(keywords),
2086 var msg = new { message = msgString };
2087 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2088 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2092 // We want the name of the provider to show up so if we don't have a manifest we create
2093 // on that at least has the provider name (I don't define any events).
2094 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2096 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2097 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2098 manifestBuilder.AddEventParameter(typeof(string), "message");
2099 manifestBuilder.EndEvent();
2100 SendManifest(manifestBuilder.CreateManifest());
2103 // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2104 fixed (char* msgStringPtr = msgString)
2106 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2107 EventProvider.EventData data = new EventProvider.EventData();
2108 data.Ptr = (ulong)msgStringPtr;
2109 data.Size = (uint)(2 * (msgString.Length + 1));
2111 m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
2115 #endif // FEATURE_MANAGED_ETW
2119 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2120 /// while writing the message to any one of the listeners will be silently ignored.
2122 private void WriteStringToAllListeners(string eventName, string msg)
2124 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2125 eventCallbackArgs.EventId = 0;
2126 eventCallbackArgs.Message = msg;
2127 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2128 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2129 eventCallbackArgs.EventName = eventName;
2131 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2133 bool dispatcherEnabled = false;
2134 if (dispatcher.m_EventEnabled == null)
2136 // if the listeners that weren't correctly initialized, we will send to it
2137 // since this is an error message and we want to see it go out.
2138 dispatcherEnabled = true;
2142 // if there's *any* enabled event on the dispatcher we'll write out the string
2143 // otherwise we'll treat the listener as disabled and skip it
2144 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2146 if (dispatcher.m_EventEnabled[evtId])
2148 dispatcherEnabled = true;
2155 if (dispatcherEnabled)
2156 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2160 // ignore any exceptions thrown by listeners' OnEventWritten
2166 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2167 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2169 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2174 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2175 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2177 #if FEATURE_MANAGED_ETW_CHANNELS
2178 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2180 EventChannel channel = EventChannel.None;
2183 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2186 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2187 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2192 // does is pass the level test?
2193 if ((currentLevel != 0) && (currentLevel < eventLevel))
2196 // if yes, does it pass the keywords test?
2197 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2199 #if FEATURE_MANAGED_ETW_CHANNELS
2200 // is there a channel with keywords that match currentMatchAnyKeyword?
2201 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2203 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2204 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2210 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2217 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2218 private void ThrowEventSourceException(string eventName, Exception innerEx = null)
2220 // If we fail during out of band logging we may end up trying
2221 // to throw another EventSourceException, thus hitting a StackOverflowException.
2222 // Avoid StackOverflow by making sure we do not recursively call this method.
2223 if (m_EventSourceExceptionRecurenceCount > 0)
2227 m_EventSourceExceptionRecurenceCount++;
2229 string errorPrefix = "EventSourceException";
2230 if (eventName != null)
2232 errorPrefix += " while processing event \"" + eventName + "\"";
2235 // TODO Create variations of EventSourceException that indicate more information using the error code.
2236 switch (EventProvider.GetLastWriteEventError())
2238 case EventProvider.WriteEventErrorCode.EventTooBig:
2239 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_EventTooBig, true);
2240 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_EventTooBig, innerEx);
2242 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2243 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NoFreeBuffers, true);
2244 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NoFreeBuffers, innerEx);
2246 case EventProvider.WriteEventErrorCode.NullInput:
2247 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NullInput, true);
2248 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NullInput, innerEx);
2250 case EventProvider.WriteEventErrorCode.TooManyArgs:
2251 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_TooManyArgs, true);
2252 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_TooManyArgs, innerEx);
2255 if (innerEx != null)
2257 innerEx = innerEx.GetBaseException();
2258 ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2261 ReportOutOfBandMessage(errorPrefix, true);
2262 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2268 m_EventSourceExceptionRecurenceCount--;
2272 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
2274 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2275 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2276 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2278 ThrowEventSourceException(eventName);
2282 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2284 if (opcode == EventOpcode.Info && eventName != null)
2286 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2288 return EventOpcode.Start;
2290 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2292 return EventOpcode.Stop;
2299 #if FEATURE_MANAGED_ETW
2301 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2303 private class OverideEventProvider : EventProvider
2305 public OverideEventProvider(EventSource eventSource, EventProviderType providerType)
2306 : base(providerType)
2308 this.m_eventSource = eventSource;
2309 this.m_eventProviderType = providerType;
2311 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2312 int perEventSourceSessionId, int etwSessionId)
2314 // We use null to represent the ETW EventListener.
2315 EventListener listener = null;
2316 m_eventSource.SendCommand(listener, m_eventProviderType, perEventSourceSessionId, etwSessionId,
2317 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2319 private EventSource m_eventSource;
2320 private EventProviderType m_eventProviderType;
2325 /// Used to hold all the static information about an event. This includes everything in the event
2326 /// descriptor as well as some stuff we added specifically for EventSource. see the
2327 /// code:m_eventData for where we use this.
2331 EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2332 now the move to CoreLib marked them as private.
2333 While they are technically private (it's a contract used between the library and the ILC toolchain),
2334 we need them to be rooted and exported from shared library for the system to work.
2335 For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2336 root them and modify shared library definition to force export them.
2343 partial struct EventMetadata
2345 public EventDescriptor Descriptor;
2346 public IntPtr EventHandle; // EventPipeEvent handle.
2347 public EventTags Tags;
2348 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2349 public bool EnabledForETW; // is this event on for ETW?
2350 #if FEATURE_PERFTRACING
2351 public bool EnabledForEventPipe; // is this event on for EventPipe?
2354 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2355 #pragma warning disable 0649
2356 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2357 #pragma warning restore 0649
2358 public string Name; // the name of the event
2359 public string Message; // If the event has a message associated with it, this is it.
2360 public ParameterInfo[] Parameters; // TODO can we remove?
2362 public TraceLoggingEventTypes TraceLoggingEventTypes;
2363 public EventActivityOptions ActivityOptions;
2366 public EventParameterType[] ParameterTypes;
2370 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2371 // eventSource. The logic is as follows
2373 // * if Command == Update
2374 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2375 // to (if listener != null)
2376 // perEventSourceSessionId = 0 - reserved for EventListeners
2377 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2378 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2379 // Keywords that identifies the session
2380 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2381 // discriminated by etwSessionId
2382 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2383 // activity tracing across different providers (which might have different sessionIds
2384 // for the same ETW session)
2385 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2386 // eventSource. In particular, if 'enabled' is false, 'level' and
2387 // 'matchAnyKeywords' are not used.
2388 // * OnEventCommand is invoked, which may cause calls to
2389 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2390 // depending on the logic in that routine.
2391 // * else (command != Update)
2392 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2393 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2395 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2396 internal void SendCommand(EventListener listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId,
2397 EventCommand command, bool enable,
2398 EventLevel level, EventKeywords matchAnyKeyword,
2399 IDictionary<string, string> commandArguments)
2401 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, eventProviderType, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2402 lock (EventListener.EventListenersLock)
2404 if (m_completelyInited)
2406 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2407 this.m_deferredCommands = null;
2408 // We are fully initialized, do the command
2409 DoCommand(commandArgs);
2413 // We can't do the command, simply remember it and we do it when we are fully constructed.
2414 if (m_deferredCommands == null)
2415 m_deferredCommands = commandArgs; // create the first entry
2418 // We have one or more entries, find the last one and add it to that.
2419 EventCommandEventArgs lastCommand = m_deferredCommands;
2420 while (lastCommand.nextCommand != null)
2421 lastCommand = lastCommand.nextCommand;
2422 lastCommand.nextCommand = commandArgs;
2429 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2430 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2431 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2432 /// This helper actually does all actual command logic.
2434 internal void DoCommand(EventCommandEventArgs commandArgs)
2436 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2437 // We defer commands until we are completely inited. This allows error messages to be sent.
2438 Debug.Assert(m_completelyInited);
2440 #if FEATURE_MANAGED_ETW
2441 if (m_etwProvider == null) // If we failed to construct
2443 #endif // FEATURE_MANAGED_ETW
2444 #if FEATURE_PERFTRACING
2445 if (m_eventPipeProvider == null)
2449 m_outOfBandMessageCount = 0;
2450 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2453 EnsureDescriptorsInitialized();
2454 Debug.Assert(m_eventData != null);
2456 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2457 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2458 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2460 throw new ArgumentException(SR.EventSource_ListenerNotFound);
2463 if (commandArgs.Arguments == null)
2464 commandArgs.Arguments = new Dictionary<string, string>();
2466 if (commandArgs.Command == EventCommand.Update)
2468 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2469 for (int i = 0; i < m_eventData.Length; i++)
2470 EnableEventForDispatcher(commandArgs.dispatcher, commandArgs.eventProviderType, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2472 if (commandArgs.enable)
2474 if (!m_eventSourceEnabled)
2476 // EventSource turned on for the first time, simply copy the bits.
2477 m_level = commandArgs.level;
2478 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2482 // Already enabled, make it the most verbose of the existing and new filter
2483 if (commandArgs.level > m_level)
2484 m_level = commandArgs.level;
2485 if (commandArgs.matchAnyKeyword == 0)
2486 m_matchAnyKeyword = 0;
2487 else if (m_matchAnyKeyword != 0)
2488 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2492 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2493 // represent 0-based positive values
2494 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2495 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2496 bSessionEnable = false;
2498 if (commandArgs.listener == null)
2500 if (!bSessionEnable)
2501 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2502 // for "global" enable/disable (passed in with listener == null and
2503 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2504 --commandArgs.perEventSourceSessionId;
2507 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2509 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2511 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2512 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2513 Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2515 // Send the manifest if we are enabling an ETW session
2516 if (bSessionEnable && commandArgs.dispatcher == null)
2518 // eventSourceDispatcher == null means this is the ETW manifest
2520 // Note that we unconditionally send the manifest whenever we are enabled, even if
2521 // we were already enabled. This is because there may be multiple sessions active
2522 // and we can't know that all the sessions have seen the manifest.
2523 if (!SelfDescribingEvents)
2524 SendManifest(m_rawManifest);
2527 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2528 // things like log messages, or test if keywords are enabled in the callback.
2529 if (commandArgs.enable)
2531 Debug.Assert(m_eventData != null);
2532 m_eventSourceEnabled = true;
2535 this.OnEventCommand(commandArgs);
2536 var eventCommandCallback = this.m_eventCommandExecuted;
2537 if (eventCommandCallback != null)
2538 eventCommandCallback(this, commandArgs);
2540 if (!commandArgs.enable)
2542 // If we are disabling, maybe we can turn on 'quick checks' to filter
2543 // quickly. These are all just optimizations (since later checks will still filter)
2545 // There is a good chance EnabledForAnyListener are not as accurate as
2546 // they could be, go ahead and get a better estimate.
2547 for (int i = 0; i < m_eventData.Length; i++)
2549 bool isEnabledForAnyListener = false;
2550 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2552 if (dispatcher.m_EventEnabled[i])
2554 isEnabledForAnyListener = true;
2558 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2561 // If no events are enabled, disable the global enabled bit.
2562 if (!AnyEventEnabled())
2565 m_matchAnyKeyword = 0;
2566 m_eventSourceEnabled = false;
2572 #if !FEATURE_PERFTRACING
2573 if (commandArgs.Command == EventCommand.SendManifest)
2575 // TODO: should we generate the manifest here if we hadn't already?
2576 if (m_rawManifest != null)
2577 SendManifest(m_rawManifest);
2581 // These are not used for non-update commands and thus should always be 'default' values
2582 // Debug.Assert(enable == true);
2583 // Debug.Assert(level == EventLevel.LogAlways);
2584 // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2586 this.OnEventCommand(commandArgs);
2587 var eventCommandCallback = m_eventCommandExecuted;
2588 if (eventCommandCallback != null)
2589 eventCommandCallback(this, commandArgs);
2594 // When the ETW session is created after the EventSource has registered with the ETW system
2595 // we can send any error messages here.
2596 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2597 // We never throw when doing a command.
2602 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2603 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2604 /// range return false, otherwise true.
2606 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, EventProviderType eventProviderType, int eventId, bool value)
2608 if (dispatcher == null)
2610 if (eventId >= m_eventData.Length)
2612 #if FEATURE_MANAGED_ETW
2613 if (m_etwProvider != null && eventProviderType == EventProviderType.ETW)
2614 m_eventData[eventId].EnabledForETW = value;
2616 #if FEATURE_PERFTRACING
2617 if (m_eventPipeProvider != null && eventProviderType == EventProviderType.EventPipe)
2618 m_eventData[eventId].EnabledForEventPipe = value;
2623 if (eventId >= dispatcher.m_EventEnabled.Length)
2625 dispatcher.m_EventEnabled[eventId] = value;
2627 m_eventData[eventId].EnabledForAnyListener = true;
2633 /// Returns true if any event at all is on.
2635 private bool AnyEventEnabled()
2637 for (int i = 0; i < m_eventData.Length; i++)
2638 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener
2639 #if FEATURE_PERFTRACING
2640 || m_eventData[i].EnabledForEventPipe
2641 #endif // FEATURE_PERFTRACING
2647 private bool IsDisposed
2649 get { return m_eventSourceDisposed; }
2652 private void EnsureDescriptorsInitialized()
2654 #if !ES_BUILD_STANDALONE
2655 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
2657 if (m_eventData == null)
2659 Guid eventSourceGuid = Guid.Empty;
2660 string eventSourceName = null;
2661 EventMetadata[] eventData = null;
2662 byte[] manifest = null;
2664 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
2665 // to the reflection approach.
2666 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
2668 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
2670 // GetMetadata failed, so we have to set it via reflection.
2671 Debug.Assert(m_rawManifest == null);
2673 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
2674 Debug.Assert(m_eventData != null);
2679 // GetMetadata worked, so set the fields as appropriate.
2680 m_name = eventSourceName;
2681 m_guid = eventSourceGuid;
2682 m_eventData = eventData;
2683 m_rawManifest = manifest;
2685 // TODO Enforce singleton pattern
2686 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
2688 EventSource eventSource = eventSourceRef.Target as EventSource;
2689 if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
2691 if (eventSource != this)
2693 throw new ArgumentException(SR.Format(SR.EventSource_EventSourceGuidInUse, m_guid));
2698 // Make certain all dispatchers also have their arrays initialized
2699 EventDispatcher dispatcher = m_Dispatchers;
2700 while (dispatcher != null)
2702 if (dispatcher.m_EventEnabled == null)
2703 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
2704 dispatcher = dispatcher.m_Next;
2706 #if FEATURE_PERFTRACING
2707 // Initialize the EventPipe event handles.
2708 DefineEventPipeEvents();
2711 if (s_currentPid == 0)
2713 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL && !CORECLR
2714 // for non-BCL EventSource we must assert SecurityPermission
2715 new SecurityPermission(PermissionState.Unrestricted).Assert();
2717 s_currentPid = Win32Native.GetCurrentProcessId();
2721 // Send out the ETW manifest XML out to ETW
2722 // Today, we only send the manifest to ETW, custom listeners don't get it.
2723 private unsafe bool SendManifest(byte[] rawManifest)
2725 bool success = true;
2727 if (rawManifest == null)
2730 Debug.Assert(!SelfDescribingEvents);
2732 #if FEATURE_MANAGED_ETW
2733 fixed (byte* dataPtr = rawManifest)
2735 // we don't want the manifest to show up in the event log channels so we specify as keywords
2736 // everything but the first 8 bits (reserved for the 8 channels)
2737 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
2738 ManifestEnvelope envelope = new ManifestEnvelope();
2740 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
2741 envelope.MajorVersion = 1;
2742 envelope.MinorVersion = 0;
2743 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
2744 int dataLeft = rawManifest.Length;
2745 envelope.ChunkNumber = 0;
2747 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
2749 dataDescrs[0].Ptr = (ulong)&envelope;
2750 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
2751 dataDescrs[0].Reserved = 0;
2753 dataDescrs[1].Ptr = (ulong)dataPtr;
2754 dataDescrs[1].Reserved = 0;
2756 int chunkSize = ManifestEnvelope.MaxChunkSize;
2757 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
2758 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
2759 while (dataLeft > 0)
2761 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
2762 if (m_etwProvider != null)
2764 if (!m_etwProvider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
2766 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
2767 // can fail. If we get this failure on the first chunk try again with something smaller
2768 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
2769 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
2771 if (envelope.ChunkNumber == 0 && chunkSize > 256)
2773 chunkSize = chunkSize / 2;
2774 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
2778 if (ThrowOnEventWriteErrors)
2779 ThrowEventSourceException("SendManifest");
2783 dataLeft -= chunkSize;
2784 dataDescrs[1].Ptr += (uint)chunkSize;
2785 envelope.ChunkNumber++;
2787 // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
2788 // 5 chunks, so only the largest manifests will hit the pause.
2789 if ((envelope.ChunkNumber % 5) == 0)
2791 #if ES_BUILD_STANDALONE
2794 RuntimeThread.Sleep(15);
2799 #endif // FEATURE_MANAGED_ETW
2804 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
2806 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
2810 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
2811 // When that is the case, we have the build the custom assemblies on a member by hand.
2812 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
2815 // On ProjectN, ReflectionOnly() always equals false. AllowEventSourceOverride is an option that allows either Microsoft.Diagnostics.Tracing or
2816 // System.Diagnostics.Tracing EventSource to be considered valid. This should not mattter anywhere but in Microsoft.Diagnostics.Tracing (nuget package).
2817 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
2818 #endif // !ES_BUILD_PN
2820 // Let the runtime to the work for us, since we can execute code in this context.
2821 Attribute firstAttribute = null;
2822 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
2824 firstAttribute = (Attribute)attribute;
2827 return firstAttribute;
2830 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2831 // In the reflection only context, we have to do things by hand.
2832 string fullTypeNameToFind = attributeType.FullName;
2834 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
2835 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
2838 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
2840 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
2842 Attribute attr = null;
2844 Debug.Assert(data.ConstructorArguments.Count <= 1);
2846 if (data.ConstructorArguments.Count == 1)
2848 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
2850 else if (data.ConstructorArguments.Count == 0)
2852 attr = (Attribute)Activator.CreateInstance(attributeType);
2857 Type t = attr.GetType();
2859 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
2861 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
2862 object value = namedArgument.TypedValue.Value;
2864 if (p.PropertyType.IsEnum)
2866 value = Enum.Parse(p.PropertyType, value.ToString());
2869 p.SetValue(attr, value, null);
2878 #else // ES_BUILD_PCL && ES_BUILD_PN
2879 // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
2880 throw new ArgumentException("EventSource_PCLPlatformNotSupportedReflection", "EventSource");
2885 /// Evaluates if two related "EventSource"-domain types should be considered the same
2887 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
2888 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
2889 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
2890 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
2892 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
2893 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
2896 // are these the same type?
2897 attributeType == reflectedAttributeType ||
2898 // are the full typenames equal?
2899 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
2900 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
2901 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
2902 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
2903 attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
2904 (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
2905 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
2906 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
2911 private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
2913 // return false for "object" and interfaces
2914 if (eventSourceType.BaseType() == null)
2917 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
2920 eventSourceType = eventSourceType.BaseType();
2922 while (eventSourceType != null && eventSourceType.IsAbstract());
2924 if (eventSourceType != null)
2926 if (!allowEventSourceOverride)
2928 if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
2929 !reflectionOnly && eventSourceType != typeof(EventSource))
2934 if (eventSourceType.Name != "EventSource")
2938 return eventSourceType;
2941 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
2942 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
2943 // at run time. 'source' is the event source to place the descriptors. If it is null,
2944 // then the descriptors are not creaed, and just the manifest is generated.
2945 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
2946 EventManifestOptions flags = EventManifestOptions.None)
2948 ManifestBuilder manifest = null;
2949 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
2950 Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
2953 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
2956 #if DEBUG && ES_BUILD_STANDALONE && TEST_SUPPORT
2957 TestSupport.TestHooks.MaybeThrow(eventSourceType,
2958 TestSupport.Category.ManifestError,
2959 "EventSource_CreateManifestAndDescriptors",
2960 new ArgumentException("EventSource_CreateManifestAndDescriptors"));
2965 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
2966 EventAttribute defaultEventAttribute;
2967 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
2968 EventMetadata[] eventData = null;
2969 Dictionary<string, string> eventsByName = null;
2970 if (source != null || (flags & EventManifestOptions.Strict) != 0)
2972 eventData = new EventMetadata[methods.Length + 1];
2973 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
2976 // See if we have localization information.
2977 ResourceManager resources = null;
2978 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
2979 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
2980 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
2982 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
2985 // Add an entry unconditionally for event ID 0 which will be for a string message.
2986 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2987 manifest.AddEventParameter(typeof(string), "message");
2988 manifest.EndEvent();
2990 // eventSourceType must be sealed and must derive from this EventSource
2991 if ((flags & EventManifestOptions.Strict) != 0)
2993 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
2997 manifest.ManifestError(SR.EventSource_TypeMustDeriveFromEventSource);
2999 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3001 manifest.ManifestError(SR.EventSource_TypeMustBeSealedOrAbstract);
3005 // Collect task, opcode, keyword and channel information
3006 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3007 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3009 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3012 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3013 if (nestedType != null)
3015 if (eventSourceType.IsAbstract())
3017 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareKTOC, nestedType.Name));
3021 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3023 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3028 // ensure we have keywords for the session-filtering reserved bits
3030 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3031 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3032 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3033 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3036 if (eventSourceType != typeof(EventSource))
3038 for (int i = 0; i < methods.Length; i++)
3040 MethodInfo method = methods[i];
3041 ParameterInfo[] args = method.GetParameters();
3043 // Get the EventDescriptor (from the Custom attributes)
3044 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3046 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3047 // the only reason of limiting the number of methods considered to be events. This broke a common
3048 // design of having event sources implement specific interfaces. To fix this in a compatible way
3049 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3050 // as they are marked with the [Event] attribute
3051 if (/* method.IsVirtual || */ method.IsStatic)
3056 if (eventSourceType.IsAbstract())
3058 if (eventAttribute != null)
3060 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareEventMethods, method.Name, eventAttribute.EventId));
3064 else if (eventAttribute == null)
3066 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3067 // (see Compat comment above)
3068 if (method.ReturnType != typeof(void))
3073 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3074 // (see Compat comment above)
3075 if (method.IsVirtual)
3080 // If we explicitly mark the method as not being an event, then honor that.
3081 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3084 defaultEventAttribute = new EventAttribute(eventId);
3085 eventAttribute = defaultEventAttribute;
3087 else if (eventAttribute.EventId <= 0)
3089 manifest.ManifestError(SR.Format(SR.EventSource_NeedPositiveId, method.Name), true);
3090 continue; // don't validate anything else for this event
3092 if (method.Name.LastIndexOf('.') >= 0)
3094 manifest.ManifestError(SR.Format(SR.EventSource_EventMustNotBeExplicitImplementation, method.Name, eventAttribute.EventId));
3098 string eventName = method.Name;
3100 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3102 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3103 bool noTask = (eventAttribute.Task == EventTask.None);
3105 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3107 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3108 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3109 if (!eventAttribute.IsOpcodeSet)
3110 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3112 // Make the stop opcode have the same task as the start opcode.
3115 if (eventAttribute.Opcode == EventOpcode.Start)
3117 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3118 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3119 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3121 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3122 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3123 manifest.AddTask(taskName, (int)eventAttribute.Task);
3126 else if (eventAttribute.Opcode == EventOpcode.Stop)
3128 // Find the start associated with this stop event. We require start to be immediately before the stop
3129 int startEventId = eventAttribute.EventId - 1;
3130 if (eventData != null && startEventId < eventData.Length)
3132 Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3133 EventMetadata startEventMetadata = eventData[startEventId];
3135 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3136 // Ideally we would throw an error
3137 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3138 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3139 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3140 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3143 // Make the stop event match the start event
3144 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3148 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3150 throw new ArgumentException(SR.EventSource_StopsFollowStarts);
3156 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3157 if (!(source != null && source.SelfDescribingEvents))
3159 manifest.StartEvent(eventName, eventAttribute);
3160 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3162 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3164 manifest.EndEvent();
3167 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3169 // Do checking for user errors (optional, but not a big deal so we do it).
3170 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3172 #if FEATURE_MANAGED_ETW_CHANNELS
3173 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3174 // and is not required for the manifest
3175 if (eventAttribute.Channel != EventChannel.None)
3179 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3183 string eventKey = "event_" + eventName;
3184 string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3185 // overwrite inline message with the localized message
3186 if (msg != null) eventAttribute.Message = msg;
3188 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3193 // Tell the TraceLogging stuff where to start allocating its own IDs.
3194 NameInfo.ReserveEventIDsBelow(eventId);
3198 TrimEventDescriptors(ref eventData);
3199 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3200 #if FEATURE_MANAGED_ETW_CHANNELS
3201 source.m_channelData = manifest.GetChannelData();
3205 // if this is an abstract event source we've already performed all the validation we can
3206 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3208 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3209 #if FEATURE_MANAGED_ETW_CHANNELS
3210 || manifest.GetChannelData().Length > 0
3214 // if the manifest is not needed and we're not requested to validate the event source return early
3215 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3218 res = manifest.CreateManifest();
3223 // if this is a runtime manifest generation let the exception propagate
3224 if ((flags & EventManifestOptions.Strict) == 0)
3226 // else store it to include it in the Argument exception we raise below
3230 if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3232 string msg = string.Empty;
3233 if (manifest.Errors.Count > 0)
3235 bool firstError = true;
3236 foreach (string error in manifest.Errors)
3239 msg += Environment.NewLine;
3245 msg = "Unexpected error: " + exception.Message;
3247 throw new ArgumentException(msg, exception);
3250 return bNeedsManifest ? res : null;
3253 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3255 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3256 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3257 string.Equals(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase))
3259 var newargs = new ParameterInfo[args.Length - 1];
3260 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3269 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3271 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3273 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3274 Type staticFieldType = staticField.FieldType;
3275 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3277 if (providerEnumKind != "Opcodes") goto Error;
3278 int value = (int)staticField.GetRawConstantValue();
3279 manifest.AddOpcode(staticField.Name, value);
3281 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3283 if (providerEnumKind != "Tasks") goto Error;
3284 int value = (int)staticField.GetRawConstantValue();
3285 manifest.AddTask(staticField.Name, value);
3287 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3289 if (providerEnumKind != "Keywords") goto Error;
3290 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3291 manifest.AddKeyword(staticField.Name, value);
3293 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3294 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3296 if (providerEnumKind != "Channels") goto Error;
3297 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3298 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3303 manifest.ManifestError(SR.Format(SR.EventSource_EnumKindMismatch, staticField.Name, staticField.FieldType.Name, providerEnumKind));
3306 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3307 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3308 // it is populated if we need to look up message resources
3309 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3310 EventAttribute eventAttribute, ParameterInfo[] eventParameters,
3311 bool hasRelatedActivityID)
3313 if (eventData == null || eventData.Length <= eventAttribute.EventId)
3315 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3316 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3317 eventData = newValues;
3320 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3321 eventAttribute.EventId,
3322 eventAttribute.Version,
3323 #if FEATURE_MANAGED_ETW_CHANNELS
3324 (byte)eventAttribute.Channel,
3328 (byte)eventAttribute.Level,
3329 (byte)eventAttribute.Opcode,
3330 (int)eventAttribute.Task,
3331 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3333 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3334 eventData[eventAttribute.EventId].Name = eventName;
3335 eventData[eventAttribute.EventId].Parameters = eventParameters;
3336 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3337 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3338 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3339 eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero;
3342 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3343 // size after all event descriptors have been added.
3344 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3346 int idx = eventData.Length;
3350 if (eventData[idx].Descriptor.EventId != 0)
3353 if (eventData.Length - idx > 2) // allow one wasted slot.
3355 EventMetadata[] newValues = new EventMetadata[idx + 1];
3356 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3357 eventData = newValues;
3361 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3362 // when a listener gets attached to a eventSource
3363 internal void AddListener(EventListener listener)
3365 lock (EventListener.EventListenersLock)
3367 bool[] enabledArray = null;
3368 if (m_eventData != null)
3369 enabledArray = new bool[m_eventData.Length];
3370 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3371 listener.OnEventSourceCreated(this);
3375 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3376 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3377 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3378 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3379 ManifestBuilder manifest, EventManifestOptions options)
3381 int evtId = eventAttribute.EventId;
3382 string evtName = method.Name;
3383 int eventArg = GetHelperCallFirstArg(method);
3384 if (eventArg >= 0 && evtId != eventArg)
3386 manifest.ManifestError(SR.Format(SR.EventSource_MismatchIdToWriteEvent, evtName, evtId, eventArg), true);
3389 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3391 manifest.ManifestError(SR.Format(SR.EventSource_EventIdReused, evtName, evtId, eventData[evtId].Name), true);
3394 // We give a task to things if they don't have one.
3395 // TODO this is moderately expensive (N*N). We probably should not even bother....
3396 Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3397 for (int idx = 0; idx < eventData.Length; ++idx)
3399 // skip unused Event IDs.
3400 if (eventData[idx].Name == null)
3403 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3405 manifest.ManifestError(SR.Format(SR.EventSource_TaskOpcodePairReused,
3406 evtName, evtId, eventData[idx].Name, idx));
3407 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3408 if ((options & EventManifestOptions.Strict) == 0)
3413 // for non-default event opcodes the user must define a task!
3414 if (eventAttribute.Opcode != EventOpcode.Info)
3416 bool failure = false;
3417 if (eventAttribute.Task == EventTask.None)
3421 // If you have the auto-assigned Task, then you did not explicitly set one.
3422 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3423 // But all other cases we want to catch the omission.
3424 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3425 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3430 manifest.ManifestError(SR.Format(SR.EventSource_EventMustHaveTaskIfNonDefaultOpcode, evtName, evtId));
3434 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3435 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3436 // by eventRegister.exe)
3437 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3438 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3440 // throw new WarningException(SR.EventSource_EventNameDoesNotEqualTaskPlusOpcode);
3443 if (eventsByName == null)
3444 eventsByName = new Dictionary<string, string>();
3446 if (eventsByName.ContainsKey(evtName))
3448 manifest.ManifestError(SR.Format(SR.EventSource_EventNameReused, evtName), true);
3451 eventsByName[evtName] = evtName;
3455 /// This method looks at the IL and tries to pattern match against the standard
3456 /// 'boilerplate' event body
3458 /// { if (Enabled()) WriteEvent(#, ...) }
3460 /// If the pattern matches, it returns the literal number passed as the first parameter to
3461 /// the WriteEvent. This is used to find common user errors (mismatching this
3462 /// number with the EventAttribute ID). It is only used for validation.
3464 /// <param name="method">The method to probe.</param>
3465 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3466 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3467 private static int GetHelperCallFirstArg(MethodInfo method)
3469 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3470 // Currently searches for the following pattern
3472 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3475 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3477 // NOP // 0 or more times
3480 // If we find this pattern we return the XXX. Otherwise we return -1.
3482 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3484 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3486 for (int idx = 0; idx < instrs.Length;)
3488 switch (instrs[idx])
3511 case 21: // LDC_I4_M1
3512 case 22: // LDC_I4_0
3513 case 23: // LDC_I4_1
3514 case 24: // LDC_I4_2
3515 case 25: // LDC_I4_3
3516 case 26: // LDC_I4_4
3517 case 27: // LDC_I4_5
3518 case 28: // LDC_I4_6
3519 case 29: // LDC_I4_7
3520 case 30: // LDC_I4_8
3521 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3522 retVal = instrs[idx] - 22;
3524 case 31: // LDC_I4_S
3525 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3526 retVal = instrs[idx + 1];
3539 // Is this call just before return?
3540 for (int search = idx + 1; search < instrs.Length; search++)
3542 if (instrs[search] == 42) // RET
3544 if (instrs[search] != 0) // NOP
3550 case 44: // BRFALSE_S
3551 case 45: // BRTRUE_S
3560 case 103: // CONV_I1
3561 case 104: // CONV_I2
3562 case 105: // CONV_I4
3563 case 106: // CONV_I8
3564 case 109: // CONV_U4
3565 case 110: // CONV_U8
3571 case 162: // STELEM_REF
3575 // Covers the CEQ instructions used in debug code for some reason.
3576 if (idx >= instrs.Length || instrs[idx] >= 6)
3580 /* Debug.Fail("Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3581 " at " + idx + " in method " + method.Name); */
3590 #if false // This routine is not needed at all, it was used for unit test debugging.
3591 [Conditional("DEBUG")]
3592 private static void OutputDebugString(string msg)
3595 msg = msg.TrimEnd('\r', '\n') +
3596 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
3597 System.Diagnostics.Debugger.Log(0, null, msg);
3603 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3604 /// It will do this even if the EventSource is not enabled.
3605 /// TODO remove flush parameter it is not used.
3607 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3608 internal void ReportOutOfBandMessage(string msg, bool flush)
3612 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3613 // send message to debugger without delay
3614 System.Diagnostics.Debugger.Log(0, null, string.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
3617 // Send it to all listeners.
3618 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3619 m_outOfBandMessageCount++;
3622 if (m_outOfBandMessageCount == 16)
3624 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3625 msg = "Reached message limit. End of EventSource error messages.";
3628 WriteEventString(EventLevel.LogAlways, -1, msg);
3629 WriteStringToAllListeners("EventSourceMessage", msg);
3631 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3634 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3636 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3637 EventSourceSettings.EtwSelfDescribingEventFormat;
3638 if ((settings & evtFormatMask) == evtFormatMask)
3640 throw new ArgumentException(SR.EventSource_InvalidEventFormat, nameof(settings));
3643 // If you did not explicitly ask for manifest, you get self-describing.
3644 if ((settings & evtFormatMask) == 0)
3645 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3649 private bool ThrowOnEventWriteErrors
3651 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
3654 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
3655 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
3659 private bool SelfDescribingEvents
3663 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
3664 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
3665 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
3671 m_config |= EventSourceSettings.EtwManifestEventFormat;
3672 m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
3676 m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
3677 m_config &= ~EventSourceSettings.EtwManifestEventFormat;
3682 // private instance state
3683 private string m_name; // My friendly name (privided in ctor)
3684 internal int m_id; // A small integer that is unique to this instance.
3685 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
3686 internal volatile EventMetadata[] m_eventData; // None per-event data
3687 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
3689 private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
3691 private EventSourceSettings m_config; // configuration information
3693 private bool m_eventSourceDisposed; // has Dispose been called.
3696 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
3697 internal EventLevel m_level; // highest level enabled by any output dispatcher
3698 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
3700 // Dispatching state
3701 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
3702 #if FEATURE_MANAGED_ETW
3703 private volatile OverideEventProvider m_etwProvider; // This hooks up ETW commands to our 'OnEventCommand' callback
3705 #if FEATURE_PERFTRACING
3706 private volatile OverideEventProvider m_eventPipeProvider;
3708 private bool m_completelyInited; // The EventSource constructor has returned without exception.
3709 private Exception m_constructionException; // If there was an exception construction, this is it
3710 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
3711 private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
3713 private string[] m_traits; // Used to implement GetTraits
3715 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
3717 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
3720 private static bool m_EventSourceInDecodeObject = false;
3722 #if FEATURE_MANAGED_ETW_CHANNELS
3723 internal volatile ulong[] m_channelData;
3726 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
3727 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
3728 ActivityTracker m_activityTracker;
3729 internal const string s_ActivityStartSuffix = "Start";
3730 internal const string s_ActivityStopSuffix = "Stop";
3732 // used for generating GUID from eventsource name
3733 private static readonly byte[] namespaceBytes = new byte[] {
3734 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
3735 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
3742 /// Enables specifying event source configuration options to be used in the EventSource constructor.
3745 public enum EventSourceSettings
3748 /// This specifies none of the special configuration options should be enabled.
3752 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
3754 ThrowOnEventWriteErrors = 1,
3756 /// Setting this option is a directive to the ETW listener should use manifest-based format when
3757 /// firing events. This is the default option when defining a type derived from EventSource
3758 /// (using the protected EventSource constructors).
3759 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3761 EtwManifestEventFormat = 4,
3763 /// Setting this option is a directive to the ETW listener should use self-describing event format
3764 /// when firing events. This is the default option when creating a new instance of the EventSource
3765 /// type (using the public EventSource constructors).
3766 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3768 EtwSelfDescribingEventFormat = 8,
3772 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
3773 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
3774 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
3775 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
3776 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
3777 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
3780 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
3781 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
3782 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
3783 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
3785 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
3786 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
3788 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
3789 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
3790 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
3791 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
3792 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
3794 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
3795 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
3796 /// that eventSource what events that dispatcher will receive.
3798 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
3799 /// override this method to do something useful with the data.
3801 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
3802 /// invariant associated with this callback is that every eventSource gets exactly one
3803 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
3804 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
3805 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
3809 public class EventListener : IDisposable
3811 private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
3814 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
3815 /// This can happen for all existing EventSources when the EventListener is created
3816 /// as well as for any EventSources that come into existence after the EventListener
3817 /// has been created.
3819 /// These 'catch up' events are called during the construction of the EventListener.
3820 /// Subclasses need to be prepared for that.
3822 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
3823 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
3825 public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
3829 CallBackForExistingEventSources(false, value);
3831 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
3835 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
3840 /// This event is raised whenever an event has been written by a EventSource for which
3841 /// the EventListener has enabled events.
3843 public event EventHandler<EventWrittenEventArgs> EventWritten;
3846 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
3849 public EventListener()
3851 // This will cause the OnEventSourceCreated callback to fire.
3852 CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener((EventListener)obj));
3856 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
3857 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
3858 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
3859 /// call Dispose when they are done with their logging.
3861 #if ES_BUILD_STANDALONE
3862 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
3864 public virtual void Dispose()
3866 lock (EventListenersLock)
3868 if (s_Listeners != null)
3870 if (this == s_Listeners)
3872 EventListener cur = s_Listeners;
3873 s_Listeners = this.m_Next;
3874 RemoveReferencesToListenerInEventSources(cur);
3878 // Find 'this' from the s_Listeners linked list.
3879 EventListener prev = s_Listeners;
3882 EventListener cur = prev.m_Next;
3887 // Found our Listener, remove references to to it in the eventSources
3888 prev.m_Next = cur.m_Next; // Remove entry.
3889 RemoveReferencesToListenerInEventSources(cur);
3899 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
3900 // 'cleanup' associated with this object
3903 /// Enable all events from the eventSource identified by 'eventSource' to the current
3904 /// dispatcher that have a verbosity level of 'level' or lower.
3906 /// This call can have the effect of REDUCING the number of events sent to the
3907 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
3909 /// This call never has an effect on other EventListeners.
3912 public void EnableEvents(EventSource eventSource, EventLevel level)
3914 EnableEvents(eventSource, level, EventKeywords.None);
3917 /// Enable all events from the eventSource identified by 'eventSource' to the current
3918 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
3919 /// matching any of the bits in 'matchAnyKeyword'.
3921 /// This call can have the effect of REDUCING the number of events sent to the
3922 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
3923 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
3925 /// This call never has an effect on other EventListeners.
3927 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
3929 EnableEvents(eventSource, level, matchAnyKeyword, null);
3932 /// Enable all events from the eventSource identified by 'eventSource' to the current
3933 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
3934 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
3935 /// effect passing additional 'key-value' arguments 'arguments' might have.
3937 /// This call can have the effect of REDUCING the number of events sent to the
3938 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
3939 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
3941 /// This call never has an effect on other EventListeners.
3943 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
3945 if (eventSource == null)
3947 throw new ArgumentNullException(nameof(eventSource));
3950 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
3952 #if FEATURE_PERFTRACING
3953 if (eventSource.GetType() == typeof(RuntimeEventSource))
3955 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, true, level, matchAnyKeyword);
3957 #endif // FEATURE_PERFTRACING
3960 /// Disables all events coming from eventSource identified by 'eventSource'.
3962 /// This call never has an effect on other EventListeners.
3964 public void DisableEvents(EventSource eventSource)
3966 if (eventSource == null)
3968 throw new ArgumentNullException(nameof(eventSource));
3971 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
3973 #if FEATURE_PERFTRACING
3974 if (eventSource.GetType() == typeof(RuntimeEventSource))
3976 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None);
3978 #endif // FEATURE_PERFTRACING
3982 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
3983 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
3984 /// it useful to store additional information about each eventSource connected to it,
3985 /// and EventSourceIndex allows this extra information to be efficiently stored in a
3986 /// (growable) array (eg List(T)).
3988 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
3991 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
3992 /// This can happen for all existing EventSources when the EventListener is created
3993 /// as well as for any EventSources that come into existence after the EventListener
3994 /// has been created.
3996 /// These 'catch up' events are called during the construction of the EventListener.
3997 /// Subclasses need to be prepared for that.
3999 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4000 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4002 /// <param name="eventSource"></param>
4003 internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4005 EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
4006 if (callBack != null)
4008 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4009 args.EventSource = eventSource;
4010 callBack(this, args);
4015 /// This method is called whenever an event has been written by a EventSource for which
4016 /// the EventListener has enabled events.
4018 /// <param name="eventData"></param>
4019 internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4021 EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
4022 if (callBack != null)
4024 callBack(this, eventData);
4031 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4032 /// ID to the eventSource (which is simply the ordinal in the global list).
4034 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4035 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4036 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4037 /// that are in the list). This seems OK since the expectation is that EventSources
4038 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4039 /// global variables).
4041 /// <param name="newEventSource"></param>
4042 internal static void AddEventSource(EventSource newEventSource)
4044 lock (EventListenersLock)
4046 if (s_EventSources == null)
4047 s_EventSources = new List<WeakReference>(2);
4049 if (!s_EventSourceShutdownRegistered)
4051 s_EventSourceShutdownRegistered = true;
4055 // Periodically search the list for existing entries to reuse, this avoids
4056 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4058 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4060 int i = s_EventSources.Count; // Work from the top down.
4064 WeakReference weakRef = s_EventSources[i];
4065 if (!weakRef.IsAlive)
4068 weakRef.Target = newEventSource;
4075 newIndex = s_EventSources.Count;
4076 s_EventSources.Add(new WeakReference(newEventSource));
4078 newEventSource.m_id = newIndex;
4080 // Add every existing dispatcher to the new EventSource
4081 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4082 newEventSource.AddListener(listener);
4088 // Whenver we have async callbacks from native code, there is an ugly issue where
4089 // during .NET shutdown native code could be calling the callback, but the CLR
4090 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4091 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4092 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4093 // do this. This is what this callback is for.
4094 // See bug 724140 for more
4095 private static void DisposeOnShutdown(object sender, EventArgs e)
4097 lock (EventListenersLock)
4099 foreach (var esRef in s_EventSources)
4101 EventSource es = esRef.Target as EventSource;
4109 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4110 /// eventSources in the appdomain.
4112 /// The EventListenersLock must be held before calling this routine.
4114 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4116 #if !ES_BUILD_STANDALONE
4117 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4119 // Foreach existing EventSource in the appdomain
4120 foreach (WeakReference eventSourceRef in s_EventSources)
4122 EventSource eventSource = eventSourceRef.Target as EventSource;
4123 if (eventSource != null)
4125 // Is the first output dispatcher the dispatcher we are removing?
4126 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4127 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4130 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4131 EventDispatcher prev = eventSource.m_Dispatchers;
4134 EventDispatcher cur = prev.m_Next;
4137 Debug.Fail("EventSource did not have a registered EventListener!");
4140 if (cur.m_Listener == listenerToRemove)
4142 prev.m_Next = cur.m_Next; // Remove entry.
4151 #if FEATURE_PERFTRACING
4152 // Remove the listener from the EventPipe dispatcher.
4153 EventPipeEventDispatcher.Instance.RemoveEventListener(listenerToRemove);
4154 #endif // FEATURE_PERFTRACING
4158 /// Checks internal consistency of EventSources/Listeners.
4160 [Conditional("DEBUG")]
4161 internal static void Validate()
4163 lock (EventListenersLock)
4165 // Get all listeners
4166 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4167 EventListener cur = s_Listeners;
4170 allListeners.Add(cur, true);
4174 // For all eventSources
4176 foreach (WeakReference eventSourceRef in s_EventSources)
4179 EventSource eventSource = eventSourceRef.Target as EventSource;
4180 if (eventSource == null)
4182 Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4184 // None listeners on eventSources exist in the dispatcher list.
4185 EventDispatcher dispatcher = eventSource.m_Dispatchers;
4186 while (dispatcher != null)
4188 Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4189 dispatcher = dispatcher.m_Next;
4192 // Every dispatcher is on Dispatcher List of every eventSource.
4193 foreach (EventListener listener in allListeners.Keys)
4195 dispatcher = eventSource.m_Dispatchers;
4198 Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4199 if (dispatcher.m_Listener == listener)
4201 dispatcher = dispatcher.m_Next;
4209 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4210 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4211 /// the lock object)
4213 internal static object EventListenersLock
4217 if (s_EventSources == null)
4218 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4219 return s_EventSources;
4223 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
4225 lock (EventListenersLock)
4227 // Disallow creating EventListener reentrancy.
4228 if (s_CreatingListener)
4230 throw new InvalidOperationException(SR.EventSource_ListenerCreatedInsideCallback);
4235 s_CreatingListener = true;
4237 if (addToListenersList)
4239 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4240 // Those added sources see this listener.
4241 this.m_Next = s_Listeners;
4245 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4246 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4247 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4248 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4250 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4252 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4254 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4255 EventSource eventSource = eventSourceRef.Target as EventSource;
4256 if (eventSource != null)
4258 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4259 args.EventSource = eventSource;
4260 callback(this, args);
4268 s_CreatingListener = false;
4275 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
4280 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4281 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4283 internal static EventListener s_Listeners;
4285 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4286 /// remove themselves from this list this is a weak list and the GC that removes them may
4287 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4288 /// to filter those out.
4290 internal static List<WeakReference> s_EventSources;
4293 /// Used to disallow reentrancy.
4295 private static bool s_CreatingListener = false;
4298 /// Used to register AD/Process shutdown callbacks.
4300 private static bool s_EventSourceShutdownRegistered = false;
4305 /// Passed to the code:EventSource.OnEventCommand callback
4307 public class EventCommandEventArgs : EventArgs
4310 /// Gets the command for the callback.
4312 public EventCommand Command { get; internal set; }
4315 /// Gets the arguments for the callback.
4317 public IDictionary<string, string> Arguments { get; internal set; }
4320 /// Enables the event that has the specified identifier.
4322 /// <param name="eventId">Event ID of event to be enabled</param>
4323 /// <returns>true if eventId is in range</returns>
4324 public bool EnableEvent(int eventId)
4326 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4327 throw new InvalidOperationException();
4328 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, true);
4332 /// Disables the event that have the specified identifier.
4334 /// <param name="eventId">Event ID of event to be disabled</param>
4335 /// <returns>true if eventId is in range</returns>
4336 public bool DisableEvent(int eventId)
4338 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4339 throw new InvalidOperationException();
4340 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, false);
4345 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4346 EventListener listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4348 this.Command = command;
4349 this.Arguments = arguments;
4350 this.eventSource = eventSource;
4351 this.listener = listener;
4352 this.eventProviderType = eventProviderType;
4353 this.perEventSourceSessionId = perEventSourceSessionId;
4354 this.etwSessionId = etwSessionId;
4355 this.enable = enable;
4357 this.matchAnyKeyword = matchAnyKeyword;
4360 internal EventSource eventSource;
4361 internal EventDispatcher dispatcher;
4362 internal EventProviderType eventProviderType;
4364 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4365 internal EventListener listener;
4366 internal int perEventSourceSessionId;
4367 internal int etwSessionId;
4368 internal bool enable;
4369 internal EventLevel level;
4370 internal EventKeywords matchAnyKeyword;
4371 internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
4377 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4379 public class EventSourceCreatedEventArgs : EventArgs
4382 /// The EventSource that is attaching to the listener.
4384 public EventSource EventSource
4392 /// EventWrittenEventArgs is passed to the user-provided override for
4393 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4395 public class EventWrittenEventArgs : EventArgs
4398 /// The name of the event.
4400 public string EventName
4404 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4409 return m_eventSource.m_eventData[EventId].Name;
4413 m_eventName = value;
4418 /// Gets the event ID for the event that was written.
4420 public int EventId { get; internal set; }
4423 /// Gets the activity ID for the thread on which the event was written.
4425 public Guid ActivityId
4429 Guid activityId = m_activityId;
4430 if (activityId == Guid.Empty)
4432 activityId = EventSource.CurrentThreadActivityId;
4439 m_activityId = value;
4444 /// Gets the related activity ID if one was specified when the event was written.
4446 public Guid RelatedActivityId
4453 /// Gets the payload for the event.
4455 public ReadOnlyCollection<object> Payload { get; internal set; }
4458 /// Gets the payload argument names.
4460 public ReadOnlyCollection<string> PayloadNames
4464 // For contract based events we create the list lazily.
4465 // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only
4466 // do the lazy init if you know it is contract based (EventID >= 0)
4467 if (EventId >= 0 && m_payloadNames == null)
4470 var names = new List<string>();
4471 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4473 names.Add(parameter.Name);
4475 m_payloadNames = new ReadOnlyCollection<string>(names);
4478 return m_payloadNames;
4483 m_payloadNames = value;
4488 /// Gets the event source object.
4490 public EventSource EventSource { get { return m_eventSource; } }
4493 /// Gets the keywords for the event.
4495 public EventKeywords Keywords
4499 if (EventId < 0) // TraceLogging convention EventID == -1
4502 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4507 /// Gets the operation code for the event.
4509 public EventOpcode Opcode
4513 if (EventId <= 0) // TraceLogging convention EventID == -1
4515 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4520 /// Gets the task for the event.
4522 public EventTask Task
4526 if (EventId <= 0) // TraceLogging convention EventID == -1
4527 return EventTask.None;
4529 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4534 /// Any provider/user defined options associated with the event.
4536 public EventTags Tags
4540 if (EventId <= 0) // TraceLogging convention EventID == -1
4542 return m_eventSource.m_eventData[EventId].Tags;
4547 /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
4549 public string Message
4553 if (EventId <= 0) // TraceLogging convention EventID == -1
4556 return m_eventSource.m_eventData[EventId].Message;
4565 #if FEATURE_MANAGED_ETW_CHANNELS
4567 /// Gets the channel for the event.
4569 public EventChannel Channel
4573 if (EventId <= 0) // TraceLogging convention EventID == -1
4574 return EventChannel.None;
4575 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4581 /// Gets the version of the event.
4587 if (EventId <= 0) // TraceLogging convention EventID == -1
4589 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4594 /// Gets the level for the event.
4596 public EventLevel Level
4600 if (EventId <= 0) // TraceLogging convention EventID == -1
4602 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4607 internal EventWrittenEventArgs(EventSource eventSource)
4609 m_eventSource = eventSource;
4611 private string m_message;
4612 private string m_eventName;
4613 private EventSource m_eventSource;
4614 private ReadOnlyCollection<string> m_payloadNames;
4615 private Guid m_activityId;
4616 internal EventTags m_tags;
4617 internal EventOpcode m_opcode;
4618 internal EventLevel m_level;
4619 internal EventKeywords m_keywords;
4624 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4626 [AttributeUsage(AttributeTargets.Class)]
4627 public sealed class EventSourceAttribute : Attribute
4630 /// Overrides the ETW name of the event source (which defaults to the class name)
4632 public string Name { get; set; }
4635 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
4636 /// except when upgrading existing ETW providers to using event sources.
4638 public string Guid { get; set; }
4642 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
4643 /// can be localized to several languages if desired. This works by creating a ResX style string table
4644 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
4645 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
4646 /// resources. This name is the value of the LocalizationResources property.
4648 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
4649 /// using the following resource naming scheme
4651 /// <para>* event_EVENTNAME</para>
4652 /// <para>* task_TASKNAME</para>
4653 /// <para>* keyword_KEYWORDNAME</para>
4654 /// <para>* map_MAPNAME</para>
4656 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
4657 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
4658 /// which represent the payload values.
4661 public string LocalizationResources { get; set; }
4665 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
4666 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
4667 /// name of the method and its signature to generate basic schema information for the event. The
4668 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
4671 [AttributeUsage(AttributeTargets.Method)]
4672 public sealed class EventAttribute : Attribute
4674 /// <summary>Construct an EventAttribute with specified eventId</summary>
4675 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
4676 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
4677 /// <summary>Event's ID</summary>
4678 public int EventId { get; private set; }
4679 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
4680 public EventLevel Level { get; set; }
4681 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
4682 public EventKeywords Keywords { get; set; }
4683 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
4684 public EventOpcode Opcode
4692 this.m_opcode = value;
4693 this.m_opcodeSet = true;
4697 internal bool IsOpcodeSet
4705 /// <summary>Event's task: allows logical grouping of events</summary>
4706 public EventTask Task { get; set; }
4707 #if FEATURE_MANAGED_ETW_CHANNELS
4708 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
4709 public EventChannel Channel { get; set; }
4711 /// <summary>Event's version</summary>
4712 public byte Version { get; set; }
4715 /// This can be specified to enable formatting and localization of the event's payload. You can
4716 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
4717 /// with the 'ToString()' of the corresponding part of the event payload.
4719 public string Message { get; set; }
4722 /// User defined options associated with the event. These do not have meaning to the EventSource but
4723 /// are passed through to listeners which given them semantics.
4725 public EventTags Tags { get; set; }
4728 /// Allows fine control over the Activity IDs generated by start and stop events
4730 public EventActivityOptions ActivityOptions { get; set; }
4733 EventOpcode m_opcode;
4734 private bool m_opcodeSet;
4739 /// By default all instance methods in a class that subclasses code:EventSource that and return
4740 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
4741 /// the code:NonEventAttribute
4743 [AttributeUsage(AttributeTargets.Method)]
4744 public sealed class NonEventAttribute : Attribute
4747 /// Constructs a default NonEventAttribute
4749 public NonEventAttribute() { }
4752 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
4753 #if FEATURE_MANAGED_ETW_CHANNELS
4755 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
4756 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
4758 /// public static class Channels
4760 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
4761 /// public const EventChannel Admin = (EventChannel)16;
4763 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
4764 /// public const EventChannel Operational = (EventChannel)17;
4768 [AttributeUsage(AttributeTargets.Field)]
4769 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4772 class EventChannelAttribute : Attribute
4775 /// Specified whether the channel is enabled by default
4777 public bool Enabled { get; set; }
4780 /// Legal values are in EventChannelType
4782 public EventChannelType EventChannelType { get; set; }
4784 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4786 /// Specifies the isolation for the channel
4788 public EventChannelIsolation Isolation { get; set; }
4791 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
4792 /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
4794 public string Access { get; set; }
4797 /// Allows importing channels defined in external manifests
4799 public string ImportChannel { get; set; }
4802 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
4803 // public string Name { get; set; }
4807 /// Allowed channel types
4809 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4812 enum EventChannelType
4814 /// <summary>The admin channel</summary>
4816 /// <summary>The operational channel</summary>
4818 /// <summary>The Analytic channel</summary>
4820 /// <summary>The debug channel</summary>
4824 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
4826 /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
4827 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
4828 /// access permissions for the channel and backing file.
4831 enum EventChannelIsolation
4834 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
4838 /// All channels that specify System isolation use the same ETW session
4842 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
4843 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
4844 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
4852 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
4854 public enum EventCommand
4857 /// Update EventSource state
4861 /// Request EventSource to generate and send its manifest
4875 #region private classes
4877 // holds a bitfield representing a session mask
4879 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
4880 /// is the index in the SessionMask of the bit that will be set. These can translate to
4881 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
4882 /// FromEventKeywords() methods.
4884 internal struct SessionMask
4886 public SessionMask(SessionMask m)
4887 { m_mask = m.m_mask; }
4889 public SessionMask(uint mask = 0)
4890 { m_mask = mask & MASK; }
4892 public bool IsEqualOrSupersetOf(SessionMask m)
4894 return (this.m_mask | m.m_mask) == this.m_mask;
4897 public static SessionMask All
4899 get { return new SessionMask(MASK); }
4902 public static SessionMask FromId(int perEventSourceSessionId)
4904 Debug.Assert(perEventSourceSessionId < MAX);
4905 return new SessionMask((uint)1 << perEventSourceSessionId);
4908 public ulong ToEventKeywords()
4910 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
4913 public static SessionMask FromEventKeywords(ulong m)
4915 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
4918 public bool this[int perEventSourceSessionId]
4922 Debug.Assert(perEventSourceSessionId < MAX);
4923 return (m_mask & (1 << perEventSourceSessionId)) != 0;
4927 Debug.Assert(perEventSourceSessionId < MAX);
4928 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
4929 else m_mask &= ~((uint)1 << perEventSourceSessionId);
4933 public static SessionMask operator |(SessionMask m1, SessionMask m2)
4935 return new SessionMask(m1.m_mask | m2.m_mask);
4938 public static SessionMask operator &(SessionMask m1, SessionMask m2)
4940 return new SessionMask(m1.m_mask & m2.m_mask);
4943 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
4945 return new SessionMask(m1.m_mask ^ m2.m_mask);
4948 public static SessionMask operator ~(SessionMask m)
4950 return new SessionMask(MASK & ~(m.m_mask));
4953 public static explicit operator ulong(SessionMask m)
4954 { return m.m_mask; }
4956 public static explicit operator uint(SessionMask m)
4957 { return m.m_mask; }
4959 private uint m_mask;
4961 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
4962 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
4963 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
4967 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
4968 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
4970 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
4971 /// that EventListener has activate) and a Single EventSource may also have many
4972 /// event Dispatchers (one for every EventListener that has activated it).
4974 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
4975 /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is
4976 /// associated with.
4978 internal class EventDispatcher
4980 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
4983 m_EventEnabled = eventEnabled;
4984 m_Listener = listener;
4988 readonly internal EventListener m_Listener; // The dispatcher this entry is for
4989 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
4991 // Only guaranteed to exist after a InsureInit()
4992 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
4993 // Of all listeners for that eventSource.
4997 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5001 public enum EventManifestOptions
5004 /// Only the resources associated with current UI culture are included in the manifest
5008 /// Throw exceptions for any inconsistency encountered
5012 /// Generate a "resources" node under "localization" for every satellite assembly provided
5016 /// Generate the manifest only if the event source needs to be registered on the machine,
5017 /// otherwise return null (but still perform validation if Strict is specified)
5019 OnlyIfNeededForRegistration = 0x4,
5021 /// When generating the manifest do *not* enforce the rule that the current EventSource class
5022 /// must be the base class for the user-defined type passed in. This allows validation of .net
5023 /// event sources using the new validation code
5025 AllowEventSourceOverride = 0x8,
5029 /// ManifestBuilder is designed to isolate the details of the message of the event from the
5030 /// rest of EventSource. This one happens to create XML.
5032 internal partial class ManifestBuilder
5035 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
5036 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
5038 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
5039 EventManifestOptions flags)
5041 #if FEATURE_MANAGED_ETW_CHANNELS
5042 this.providerName = providerName;
5046 this.resources = resources;
5047 sb = new StringBuilder();
5048 events = new StringBuilder();
5049 templates = new StringBuilder();
5050 opcodeTab = new Dictionary<int, string>();
5051 stringTab = new Dictionary<string, string>();
5052 errors = new List<string>();
5053 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
5055 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5056 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\">");
5057 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5058 sb.Append("<provider name=\"").Append(providerName).
5059 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
5060 if (dllName != null)
5061 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
5063 var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
5064 sb.Append("\" symbol=\"").Append(symbolsName);
5065 sb.Append("\">").AppendLine();
5068 public void AddOpcode(string name, int value)
5070 if ((flags & EventManifestOptions.Strict) != 0)
5072 if (value <= 10 || value >= 239)
5074 ManifestError(SR.Format(SR.EventSource_IllegalOpcodeValue, name, value));
5077 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5079 ManifestError(SR.Format(SR.EventSource_OpcodeCollision, name, prevName, value));
5082 opcodeTab[value] = name;
5084 public void AddTask(string name, int value)
5086 if ((flags & EventManifestOptions.Strict) != 0)
5088 if (value <= 0 || value >= 65535)
5090 ManifestError(SR.Format(SR.EventSource_IllegalTaskValue, name, value));
5093 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5095 ManifestError(SR.Format(SR.EventSource_TaskCollision, name, prevName, value));
5098 if (taskTab == null)
5099 taskTab = new Dictionary<int, string>();
5100 taskTab[value] = name;
5102 public void AddKeyword(string name, ulong value)
5104 if ((value & (value - 1)) != 0) // Is it a power of 2?
5106 ManifestError(SR.Format(SR.EventSource_KeywordNeedPowerOfTwo, "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
5108 if ((flags & EventManifestOptions.Strict) != 0)
5110 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
5112 ManifestError(SR.Format(SR.EventSource_IllegalKeywordsValue, name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5115 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5117 ManifestError(SR.Format(SR.EventSource_KeywordCollision, name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5120 if (keywordTab == null)
5121 keywordTab = new Dictionary<ulong, string>();
5122 keywordTab[value] = name;
5125 #if FEATURE_MANAGED_ETW_CHANNELS
5127 /// Add a channel. channelAttribute can be null
5129 public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
5131 EventChannel chValue = (EventChannel)value;
5132 if (value < (int)EventChannel.Admin || value > 255)
5133 ManifestError(SR.Format(SR.EventSource_EventChannelOutOfRange, name, value));
5134 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
5135 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
5137 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
5138 // but we want to allow them to override the default ones...
5139 ManifestError(SR.Format(SR.EventSource_ChannelTypeDoesNotMatchEventChannelValue,
5140 name, ((EventChannel)value).ToString()));
5143 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
5145 ulong kwd = GetChannelKeyword(chValue);
5147 if (channelTab == null)
5148 channelTab = new Dictionary<int, ChannelInfo>(4);
5149 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
5152 private EventChannelType EventChannelToChannelType(EventChannel channel)
5154 #if !ES_BUILD_STANDALONE
5155 Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
5157 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
5159 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
5161 EventChannelAttribute attrib = new EventChannelAttribute();
5162 attrib.EventChannelType = EventChannelToChannelType(channel);
5163 if (attrib.EventChannelType <= EventChannelType.Operational)
5164 attrib.Enabled = true;
5168 public ulong[] GetChannelData()
5170 if (this.channelTab == null)
5172 return new ulong[0];
5175 // We create an array indexed by the channel id for fast look up.
5176 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
5178 foreach (var item in this.channelTab.Keys)
5186 ulong[] channelMask = new ulong[maxkey + 1];
5187 foreach (var item in this.channelTab)
5189 channelMask[item.Key] = item.Value.Keywords;
5196 public void StartEvent(string eventName, EventAttribute eventAttribute)
5198 Debug.Assert(numParams == 0);
5199 Debug.Assert(this.eventName == null);
5200 this.eventName = eventName;
5202 byteArrArgIndices = null;
5204 events.Append(" <event").
5205 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
5206 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
5207 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
5208 Append(" symbol=\"").Append(eventName).Append("\"");
5210 // at this point we add to the manifest's stringTab a message that is as-of-yet
5211 // "untranslated to manifest convention", b/c we don't have the number or position
5212 // of any byte[] args (which require string format index updates)
5213 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
5215 if (eventAttribute.Keywords != 0)
5216 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
5217 if (eventAttribute.Opcode != 0)
5218 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
5219 if (eventAttribute.Task != 0)
5220 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
5221 #if FEATURE_MANAGED_ETW_CHANNELS
5222 if (eventAttribute.Channel != 0)
5224 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
5229 public void AddEventParameter(Type type, string name)
5232 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
5233 if (type == typeof(byte[]))
5235 // mark this index as "extraneous" (it has no parallel in the managed signature)
5236 // we use these values in TranslateToManifestConvention()
5237 if (byteArrArgIndices == null)
5238 byteArrArgIndices = new List<int>(4);
5239 byteArrArgIndices.Add(numParams);
5241 // add an extra field to the template representing the length of the binary blob
5243 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
5246 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
5247 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
5248 // as for 'byte[]' args (blob_arg_name + "Size")
5249 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
5251 // add "length" attribute to the "blob" field in the template (referencing the field added above)
5252 templates.Append(" length=\"").Append(name).Append("Size\"");
5254 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
5255 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(ulong) && Enum.GetUnderlyingType(type) != typeof(long))
5257 templates.Append(" map=\"").Append(type.Name).Append("\"");
5258 if (mapsTab == null)
5259 mapsTab = new Dictionary<string, Type>();
5260 if (!mapsTab.ContainsKey(type.Name))
5261 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
5264 templates.Append("/>").AppendLine();
5266 public void EndEvent()
5270 templates.Append(" </template>").AppendLine();
5271 events.Append(" template=\"").Append(eventName).Append("Args\"");
5273 events.Append("/>").AppendLine();
5275 if (byteArrArgIndices != null)
5276 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
5278 // at this point we have all the information we need to translate the C# Message
5279 // to the manifest string we'll put in the stringTab
5281 if (stringTab.TryGetValue("event_" + eventName, out msg))
5283 msg = TranslateToManifestConvention(msg, eventName);
5284 stringTab["event_" + eventName] = msg;
5289 byteArrArgIndices = null;
5292 #if FEATURE_MANAGED_ETW_CHANNELS
5293 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
5294 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
5295 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
5296 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
5297 // to channels by the OS infrastructure).
5298 // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
5299 // assumed that that the keyword for that channel should be that bit.
5300 // otherwise we allocate a channel bit for the channel.
5301 // explicit channel bits are only used by WCF to mimic an existing manifest,
5302 // so we don't dont do error checking.
5303 public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
5305 // strip off any non-channel keywords, since we are only interested in channels here.
5306 channelKeyword &= ValidPredefinedChannelKeywords;
5307 if (channelTab == null)
5309 channelTab = new Dictionary<int, ChannelInfo>(4);
5312 if (channelTab.Count == MaxCountChannels)
5313 ManifestError(SR.EventSource_MaxChannelExceeded);
5316 if (!channelTab.TryGetValue((int)channel, out info))
5318 // If we were not given an explicit channel, allocate one.
5319 if (channelKeyword != 0)
5321 channelKeyword = nextChannelKeywordBit;
5322 nextChannelKeywordBit >>= 1;
5327 channelKeyword = info.Keywords;
5330 return channelKeyword;
5334 public byte[] CreateManifest()
5336 string str = CreateManifestString();
5337 return Encoding.UTF8.GetBytes(str);
5340 public IList<string> Errors { get { return errors; } }
5343 /// When validating an event source it adds the error to the error collection.
5344 /// When not validating it throws an exception if runtimeCritical is "true".
5345 /// Otherwise the error is ignored.
5347 /// <param name="msg"></param>
5348 /// <param name="runtimeCritical"></param>
5349 public void ManifestError(string msg, bool runtimeCritical = false)
5351 if ((flags & EventManifestOptions.Strict) != 0)
5353 else if (runtimeCritical)
5354 throw new ArgumentException(msg);
5357 private string CreateManifestString()
5360 #if FEATURE_MANAGED_ETW_CHANNELS
5361 // Write out the channels
5362 if (channelTab != null)
5364 sb.Append(" <channels>").AppendLine();
5365 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
5366 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
5367 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
5368 foreach (var kvpair in sortedChannels)
5370 int channel = kvpair.Key;
5371 ChannelInfo channelInfo = kvpair.Value;
5373 string channelType = null;
5374 string elementName = "channel";
5375 bool enabled = false;
5376 string fullName = null;
5377 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5378 string isolation = null;
5379 string access = null;
5381 if (channelInfo.Attribs != null)
5383 var attribs = channelInfo.Attribs;
5384 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
5385 channelType = attribs.EventChannelType.ToString();
5386 enabled = attribs.Enabled;
5387 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5388 if (attribs.ImportChannel != null)
5390 fullName = attribs.ImportChannel;
5391 elementName = "importChannel";
5393 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
5394 isolation = attribs.Isolation.ToString();
5395 access = attribs.Access;
5398 if (fullName == null)
5399 fullName = providerName + "/" + channelInfo.Name;
5401 sb.Append(" <").Append(elementName);
5402 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
5403 sb.Append(" name=\"").Append(fullName).Append("\"");
5404 if (elementName == "channel") // not applicable to importChannels.
5406 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
5407 sb.Append(" value=\"").Append(channel).Append("\"");
5408 if (channelType != null)
5409 sb.Append(" type=\"").Append(channelType).Append("\"");
5410 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
5411 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5413 sb.Append(" access=\"").Append(access).Append("\"");
5414 if (isolation != null)
5415 sb.Append(" isolation=\"").Append(isolation).Append("\"");
5418 sb.Append("/>").AppendLine();
5420 sb.Append(" </channels>").AppendLine();
5424 // Write out the tasks
5425 if (taskTab != null)
5428 sb.Append(" <tasks>").AppendLine();
5429 var sortedTasks = new List<int>(taskTab.Keys);
5431 foreach (int task in sortedTasks)
5433 sb.Append(" <task");
5434 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
5435 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
5437 sb.Append(" </tasks>").AppendLine();
5440 // Write out the maps
5441 if (mapsTab != null)
5443 sb.Append(" <maps>").AppendLine();
5444 foreach (Type enumType in mapsTab.Values)
5446 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
5447 string mapKind = isbitmap ? "bitMap" : "valueMap";
5448 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
5450 // write out each enum value
5451 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
5452 foreach (FieldInfo staticField in staticFields)
5454 object constantValObj = staticField.GetRawConstantValue();
5455 if (constantValObj != null)
5458 if (constantValObj is int)
5459 hexValue = ((int)constantValObj);
5460 else if (constantValObj is long)
5461 hexValue = ((long)constantValObj);
5465 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
5466 // TODO: Warn people about the dropping of values.
5467 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
5470 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
5471 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
5472 sb.Append("/>").AppendLine();
5475 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
5477 sb.Append(" </maps>").AppendLine();
5480 // Write out the opcodes
5481 sb.Append(" <opcodes>").AppendLine();
5482 var sortedOpcodes = new List<int>(opcodeTab.Keys);
5483 sortedOpcodes.Sort();
5484 foreach (int opcode in sortedOpcodes)
5486 sb.Append(" <opcode");
5487 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
5488 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
5490 sb.Append(" </opcodes>").AppendLine();
5492 // Write out the keywords
5493 if (keywordTab != null)
5495 sb.Append(" <keywords>").AppendLine();
5496 var sortedKeywords = new List<ulong>(keywordTab.Keys);
5497 sortedKeywords.Sort();
5498 foreach (ulong keyword in sortedKeywords)
5500 sb.Append(" <keyword");
5501 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
5502 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
5504 sb.Append(" </keywords>").AppendLine();
5507 sb.Append(" <events>").AppendLine();
5509 sb.Append(" </events>").AppendLine();
5511 sb.Append(" <templates>").AppendLine();
5512 if (templates.Length > 0)
5514 sb.Append(templates);
5518 // Work around a cornercase ETW issue where a manifest with no templates causes
5519 // ETW events to not get sent to their associated channel.
5520 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
5522 sb.Append(" </templates>").AppendLine();
5524 sb.Append("</provider>").AppendLine();
5525 sb.Append("</events>").AppendLine();
5526 sb.Append("</instrumentation>").AppendLine();
5528 // Output the localization information.
5529 sb.Append("<localization>").AppendLine();
5531 List<CultureInfo> cultures = null;
5532 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
5534 cultures = GetSupportedCultures(resources);
5538 cultures = new List<CultureInfo>();
5539 cultures.Add(CultureInfo.CurrentUICulture);
5541 #if ES_BUILD_STANDALONE || ES_BUILD_PN
5542 var sortedStrings = new List<string>(stringTab.Keys);
5543 sortedStrings.Sort();
5546 var sortedStrings = new string[stringTab.Keys.Count];
5547 stringTab.Keys.CopyTo(sortedStrings, 0);
5548 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
5549 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
5550 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
5551 // access BinaryCompatibility.
5552 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, string.Compare);
5554 foreach (var ci in cultures)
5556 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
5557 sb.Append(" <stringTable>").AppendLine();
5559 foreach (var stringKey in sortedStrings)
5561 string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
5562 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
5564 sb.Append(" </stringTable>").AppendLine();
5565 sb.Append(" </resources>").AppendLine();
5567 sb.Append("</localization>").AppendLine();
5568 sb.AppendLine("</instrumentationManifest>");
5569 return sb.ToString();
5573 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
5575 stringBuilder.Append(" name=\"").Append(name).Append("\"");
5576 WriteMessageAttrib(sb, elementName, name, name);
5578 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
5580 string key = elementName + "_" + name;
5581 // See if the user wants things localized.
5582 if (resources != null)
5584 // resource fallback: strings in the neutral culture will take precedence over inline strings
5585 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
5586 if (localizedString != null)
5587 value = localizedString;
5592 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
5594 if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
5596 ManifestError(SR.Format(SR.EventSource_DuplicateStringKey, key), true);
5600 stringTab[key] = value;
5602 internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
5604 string value = null;
5605 if (resources != null)
5607 string localizedString = resources.GetString(key, ci);
5608 if (localizedString != null)
5610 value = localizedString;
5611 if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
5613 var evtName = key.Substring("event_".Length);
5614 value = TranslateToManifestConvention(value, evtName);
5618 if (etwFormat && value == null)
5619 stringTab.TryGetValue(key, out value);
5625 /// There's no API to enumerate all languages an assembly is localized into, so instead
5626 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
5629 /// <param name="resources"></param>
5630 /// <returns></returns>
5631 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
5633 var cultures = new List<CultureInfo>();
5635 if (!cultures.Contains(CultureInfo.CurrentUICulture))
5636 cultures.Insert(0, CultureInfo.CurrentUICulture);
5640 private static string GetLevelName(EventLevel level)
5642 return (((int)level >= 16) ? "" : "win:") + level.ToString();
5645 #if FEATURE_MANAGED_ETW_CHANNELS
5646 private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
5648 ChannelInfo info = null;
5649 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
5651 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
5652 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
5654 // allow channels to be auto-defined. The well known ones get their well known names, and the
5655 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
5656 if (channelTab == null)
5657 channelTab = new Dictionary<int, ChannelInfo>(4);
5659 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
5660 if (EventChannel.Debug < channel)
5661 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
5663 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
5664 if (!channelTab.TryGetValue((int)channel, out info))
5665 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
5667 // events that specify admin channels *must* have non-null "Message" attributes
5668 if (resources != null && eventMessage == null)
5669 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
5670 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
5671 ManifestError(SR.Format(SR.EventSource_EventWithAdminChannelMustHaveMessage, eventName, info.Name));
5675 private string GetTaskName(EventTask task, string eventName)
5677 if (task == EventTask.None)
5681 if (taskTab == null)
5682 taskTab = new Dictionary<int, string>();
5683 if (!taskTab.TryGetValue((int)task, out ret))
5684 ret = taskTab[(int)task] = eventName;
5688 private string GetOpcodeName(EventOpcode opcode, string eventName)
5692 case EventOpcode.Info:
5694 case EventOpcode.Start:
5696 case EventOpcode.Stop:
5698 case EventOpcode.DataCollectionStart:
5699 return "win:DC_Start";
5700 case EventOpcode.DataCollectionStop:
5701 return "win:DC_Stop";
5702 case EventOpcode.Extension:
5703 return "win:Extension";
5704 case EventOpcode.Reply:
5706 case EventOpcode.Resume:
5707 return "win:Resume";
5708 case EventOpcode.Suspend:
5709 return "win:Suspend";
5710 case EventOpcode.Send:
5712 case EventOpcode.Receive:
5713 return "win:Receive";
5717 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
5719 ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true);
5725 private string GetKeywords(ulong keywords, string eventName)
5727 #if FEATURE_MANAGED_ETW_CHANNELS
5728 // ignore keywords associate with channels
5729 // See ValidPredefinedChannelKeywords def for more.
5730 keywords &= ~ValidPredefinedChannelKeywords;
5734 for (ulong bit = 1; bit != 0; bit <<= 1)
5736 if ((keywords & bit) != 0)
5738 string keyword = null;
5739 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
5740 (bit >= (ulong)0x1000000000000))
5742 // do not report Windows reserved keywords in the manifest (this allows the code
5743 // to be resilient to potential renaming of these keywords)
5744 keyword = string.Empty;
5746 if (keyword == null)
5748 ManifestError(SR.Format(SR.EventSource_UndefinedKeyword, "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
5749 keyword = string.Empty;
5751 if (ret.Length != 0 && keyword.Length != 0)
5753 ret = ret + keyword;
5759 private string GetTypeName(Type type)
5763 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
5764 var typeName = GetTypeName(fields[0].FieldType);
5765 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
5768 return GetTypeNameHelper(type);
5771 private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
5773 if (stringBuilder == null)
5774 stringBuilder = new StringBuilder();
5775 stringBuilder.Append(eventMessage, startIndex, count);
5778 private static readonly string[] s_escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" };
5779 // Manifest messages use %N conventions for their message substitutions. Translate from
5780 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
5781 private string TranslateToManifestConvention(string eventMessage, string evtName)
5783 StringBuilder stringBuilder = null; // We lazily create this
5784 int writtenSoFar = 0;
5788 if (i >= eventMessage.Length)
5790 if (stringBuilder == null)
5791 return eventMessage;
5792 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
5793 return stringBuilder.ToString();
5796 if (eventMessage[i] == '%')
5798 // handle format message escaping character '%' by escaping it
5799 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
5800 stringBuilder.Append("%%");
5804 else if (i < eventMessage.Length - 1 &&
5805 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
5807 // handle C# escaped '{" and '}'
5808 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
5809 stringBuilder.Append(eventMessage[i]);
5813 else if (eventMessage[i] == '{')
5815 int leftBracket = i;
5818 while (i < eventMessage.Length && char.IsDigit(eventMessage[i]))
5820 argNum = argNum * 10 + eventMessage[i] - '0';
5823 if (i < eventMessage.Length && eventMessage[i] == '}')
5826 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
5827 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
5828 stringBuilder.Append('%').Append(manIndex);
5829 // An '!' after the insert specifier {n} will be interpreted as a literal.
5830 // We'll escape it so that mc.exe does not attempt to consider it the
5831 // beginning of a format string.
5832 if (i < eventMessage.Length && eventMessage[i] == '!')
5835 stringBuilder.Append("%!");
5841 ManifestError(SR.Format(SR.EventSource_UnsupportedMessageProperty, evtName, eventMessage));
5844 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
5846 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
5848 stringBuilder.Append(s_escapes[chIdx]);
5856 private int TranslateIndexToManifestConvention(int idx, string evtName)
5858 List<int> byteArrArgIndices;
5859 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
5861 foreach (var byArrIdx in byteArrArgIndices)
5863 if (idx >= byArrIdx)
5872 #if FEATURE_MANAGED_ETW_CHANNELS
5876 public ulong Keywords;
5877 public EventChannelAttribute Attribs;
5881 Dictionary<int, string> opcodeTab;
5882 Dictionary<int, string> taskTab;
5883 #if FEATURE_MANAGED_ETW_CHANNELS
5884 Dictionary<int, ChannelInfo> channelTab;
5886 Dictionary<ulong, string> keywordTab;
5887 Dictionary<string, Type> mapsTab;
5889 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
5891 #if FEATURE_MANAGED_ETW_CHANNELS
5892 // WCF used EventSource to mimic a existing ETW manifest. To support this
5893 // in just their case, we allowed them to specify the keywords associated
5894 // with their channels explicitly. ValidPredefinedChannelKeywords is
5895 // this set of channel keywords that we allow to be explicitly set. You
5896 // can ignore these bits otherwise.
5897 internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
5898 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
5899 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
5902 StringBuilder sb; // Holds the provider information.
5903 StringBuilder events; // Holds the events.
5904 StringBuilder templates;
5906 #if FEATURE_MANAGED_ETW_CHANNELS
5907 string providerName;
5909 ResourceManager resources; // Look up localized strings here.
5910 EventManifestOptions flags;
5911 IList<string> errors; // list of currently encountered errors
5912 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
5914 // State we track between StartEvent and EndEvent.
5915 string eventName; // Name of the event currently being processed.
5916 int numParams; // keeps track of the number of args the event has.
5917 List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
5922 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
5924 internal struct ManifestEnvelope
5926 public const int MaxChunkSize = 0xFF00;
5927 public enum ManifestFormats : byte
5929 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
5932 #if FEATURE_MANAGED_ETW
5933 public ManifestFormats Format;
5934 public byte MajorVersion;
5935 public byte MinorVersion;
5937 public ushort TotalChunks;
5938 public ushort ChunkNumber;