Re-enable CA1825 (zero-length array allocations) (dotnet/corefx#39708)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Diagnostics / Tracing / EventSource.cs
blobbe51c6880ed4426ab8d593cba028143e2ff94875
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
10 #endif
12 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
13 // 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.
17 // Basic Model
19 // PRINCIPLE: EventSource - ETW decoupling
20 //
21 // Conceptually an EventSouce is something that takes event logging data from the source methods
22 // to the EventListener that can subscribe to 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 // listens to 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:
32 //
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 supports both ways of writing events (either contract
43 // based (WriteEvent*) or dynamic (Write<T>)). Both ways fully support the same set of data
44 // types. It is recommended, 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 cases.
49 // Allowed Data:
50 //
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
54 //
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)
59 //
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)
66 //
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 obviously NOT the original objects.
71 //
72 // ETW serialization formats:
73 //
74 // As mentioned, conceptually EventSources 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 the 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
78 //
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 supports 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 affect the WriteEvent* APIs going to ETW.
92 // Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
93 //
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 and 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
132 // where it will write it to
134 // All ETW writes eventually call
135 // EventWriteTransfer
136 // EventProvider.WriteEventRaw - sets last error
137 // EventSource.WriteEventRaw - Does EventSource exception handling logic
138 // WriteMultiMerge
139 // WriteImpl<T>
140 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
141 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
143 // Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
144 // how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
145 // since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
146 // can call one of these
147 // WriteMetadata - transforms the type T into serialization meta data blob for that type
148 // WriteObjectData - transforms an object of T into serialization data blob for that instance
149 // GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
150 // The first two are used to serialize something for ETW. The second one is used to transform the object
151 // for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
152 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
154 // It is an important observation that while EventSource does support users directly calling with EventData*
155 // blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
156 // path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
158 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
160 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
161 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
162 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
163 // This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
164 // opportunity to expose this format to EventListeners in the future.
166 using System;
167 using System.Collections.Generic;
168 using System.Collections.ObjectModel;
169 using System.Diagnostics;
170 using System.Diagnostics.CodeAnalysis;
171 using System.Globalization;
172 using System.Numerics;
173 using System.Reflection;
174 using System.Resources;
175 using System.Security;
176 #if ES_BUILD_STANDALONE
177 using System.Security.Permissions;
178 #endif
180 using System.Text;
181 using System.Threading;
182 using Microsoft.Win32;
184 #if ES_BUILD_STANDALONE
185 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
186 using BitOperations = Microsoft.Diagnostics.Tracing.Internal.BitOperations;
187 #else
188 using System.Threading.Tasks;
189 #endif
191 using Microsoft.Reflection;
193 #if !ES_BUILD_AGAINST_DOTNET_V35
194 using Contract = System.Diagnostics.Contracts.Contract;
195 #else
196 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
197 #endif
199 #if ES_BUILD_STANDALONE
200 namespace Microsoft.Diagnostics.Tracing
201 #else
202 namespace System.Diagnostics.Tracing
203 #endif
205 /// <summary>
206 /// This class is meant to be inherited by a user-defined event source in order to define a managed
207 /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
208 /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
209 /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
210 /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
211 /// is sufficient for many users.
212 /// <para>
213 /// To achieve more control over the ETW provider manifest exposed by the event source type, the
214 /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
215 /// </para><para>
216 /// For very advanced EventSources, it is possible to intercept the commands being given to the
217 /// eventSource and change what filtering is done (see EventListener.EnableEvents and
218 /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
219 /// e.g. dumping a data structure (see EventSource.SendCommand and
220 /// <see cref="EventSource.OnEventCommand"/>).
221 /// </para><para>
222 /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
223 /// It is also possible to control and intercept the data dispatcher programmatically. See
224 /// <see cref="EventListener"/> for more.
225 /// </para>
226 /// </summary>
227 /// <remarks>
228 /// This is a minimal definition for a custom event source:
229 /// <code>
230 /// [EventSource(Name="Samples-Demos-Minimal")]
231 /// sealed class MinimalEventSource : EventSource
232 /// {
233 /// public static MinimalEventSource Log = new MinimalEventSource();
234 /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
235 /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
236 /// private MinimalEventSource() {}
237 /// }
238 /// </code>
239 /// </remarks>
240 public partial class EventSource : IDisposable
243 #if FEATURE_EVENTSOURCE_XPLAT
244 private static readonly EventListener? persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
245 #endif //FEATURE_EVENTSOURCE_XPLAT
247 /// <summary>
248 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
249 /// </summary>
250 public string Name { get { return m_name; } }
251 /// <summary>
252 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
253 /// </summary>
254 public Guid Guid { get { return m_guid; } }
256 /// <summary>
257 /// Returns true if the eventSource has been enabled at all. This is the preferred test
258 /// to be performed before a relatively expensive EventSource operation.
259 /// </summary>
260 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
261 public bool IsEnabled()
263 return m_eventSourceEnabled;
266 /// <summary>
267 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
268 ///
269 /// Note that the result of this function is only an approximation on whether a particular
270 /// event is active or not. It is only meant to be used as way of avoiding expensive
271 /// computation for logging when logging is not on, therefore it sometimes returns false
272 /// positives (but is always accurate when returning false). EventSources are free to
273 /// have additional filtering.
274 /// </summary>
275 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
276 public bool IsEnabled(EventLevel level, EventKeywords keywords)
278 return IsEnabled(level, keywords, EventChannel.None);
281 /// <summary>
282 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
283 /// if 'keywords' specifies a channel bit for a channel that is enabled.
284 ///
285 /// Note that the result of this function only an approximation on whether a particular
286 /// event is active or not. It is only meant to be used as way of avoiding expensive
287 /// computation for logging when logging is not on, therefore it sometimes returns false
288 /// positives (but is always accurate when returning false). EventSources are free to
289 /// have additional filtering.
290 /// </summary>
291 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
292 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
294 if (!m_eventSourceEnabled)
295 return false;
297 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
298 return false;
300 return true;
303 /// <summary>
304 /// Returns the settings for the event source instance
305 /// </summary>
306 public EventSourceSettings Settings
308 get { return m_config; }
311 // Manifest support
312 /// <summary>
313 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
314 /// This API allows you to compute this without actually creating an instance of the EventSource.
315 /// It only needs to reflect over the type.
316 /// </summary>
317 public static Guid GetGuid(Type eventSourceType)
319 if (eventSourceType == null)
320 throw new ArgumentNullException(nameof(eventSourceType));
322 EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
323 string name = eventSourceType.Name;
324 if (attrib != null)
326 if (attrib.Guid != null)
328 Guid g = Guid.Empty;
329 #if !ES_BUILD_AGAINST_DOTNET_V35
330 if (Guid.TryParse(attrib.Guid, out g))
331 return g;
332 #else
333 try { return new Guid(attrib.Guid); }
334 catch (Exception) { }
335 #endif
338 if (attrib.Name != null)
339 name = attrib.Name;
342 if (name == null)
344 throw new ArgumentException(SR.Argument_InvalidTypeName, nameof(eventSourceType));
346 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
348 /// <summary>
349 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
350 /// This API allows you to compute this without actually creating an instance of the EventSource.
351 /// It only needs to reflect over the type.
352 /// </summary>
353 public static string GetName(Type eventSourceType)
355 return GetName(eventSourceType, EventManifestOptions.None);
358 /// <summary>
359 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
360 /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema.
361 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
362 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
363 /// </summary>
364 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
365 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
366 /// which it is embedded. This parameter specifies what name will be used</param>
367 /// <returns>The XML data string</returns>
368 public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest)
370 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
372 /// <summary>
373 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
374 /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema.
375 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
376 /// ensures that the entries in the event log will be "optimally" localized.
377 /// </summary>
378 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
379 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
380 /// which it is embedded. This parameter specifies what name will be used</param>
381 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
382 /// this returns null when the eventSourceType does not require explicit registration</param>
383 /// <returns>The XML data string or null</returns>
384 public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest, EventManifestOptions flags)
386 if (eventSourceType == null)
387 throw new ArgumentNullException(nameof(eventSourceType));
389 byte[]? manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
390 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
393 // EventListener support
394 /// <summary>
395 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
396 /// </summary>
397 /// <returns></returns>
398 public static IEnumerable<EventSource> GetSources()
400 var ret = new List<EventSource>();
401 lock (EventListener.EventListenersLock)
403 Debug.Assert(EventListener.s_EventSources != null);
405 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
407 if (eventSourceRef.Target is EventSource eventSource && !eventSource.IsDisposed)
408 ret.Add(eventSource);
411 return ret;
414 /// <summary>
415 /// Send a command to a particular EventSource identified by 'eventSource'.
416 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
417 /// callback. What the EventSource does with the command and its arguments are from
418 /// that point EventSource-specific.
419 /// </summary>
420 /// <param name="eventSource">The instance of EventSource to send the command to</param>
421 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
422 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
423 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string?>? commandArguments)
425 if (eventSource == null)
426 throw new ArgumentNullException(nameof(eventSource));
428 // User-defined EventCommands should not conflict with the reserved commands.
429 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
431 throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command));
434 eventSource.SendCommand(null, EventProviderType.ETW, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
437 // Error APIs. (We don't throw by default, but you can probe for status)
438 /// <summary>
439 /// Because
440 ///
441 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
442 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
443 ///
444 /// The event source constructor does not throw exceptions. Instead we remember any exception that
445 /// was generated (it is also logged to Trace.WriteLine).
446 /// </summary>
447 public Exception? ConstructionException { get { return m_constructionException; } }
449 /// <summary>
450 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
451 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
452 /// (e.g. like the built in ETW listener). These traits are specified at EventSource
453 /// construction time and can be retrieved by using this GetTrait API.
454 /// </summary>
455 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
456 /// <returns>The value string associated with key. Will return null if there is no such key.</returns>
457 public string? GetTrait(string key)
459 if (m_traits != null)
461 for (int i = 0; i < m_traits.Length - 1; i += 2)
463 if (m_traits[i] == key)
464 return m_traits[i + 1];
468 return null;
471 /// <summary>
472 /// Displays the name and GUID for the eventSource for debugging purposes.
473 /// </summary>
474 public override string ToString()
476 return SR.Format(SR.EventSource_ToString, Name, Guid);
479 /// <summary>
480 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
481 /// </summary>
482 public event EventHandler<EventCommandEventArgs>? EventCommandExecuted
486 if (value == null)
487 return;
489 m_eventCommandExecuted += value;
491 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
492 // It should get a chance to handle the deferred commands.
493 EventCommandEventArgs? deferredCommands = m_deferredCommands;
494 while (deferredCommands != null)
496 value(this, deferredCommands);
497 deferredCommands = deferredCommands.nextCommand;
500 remove
502 m_eventCommandExecuted -= value;
506 #region ActivityID
508 /// <summary>
509 /// When a thread starts work that is on behalf of 'something else' (typically another
510 /// thread or network request) it should mark the thread as working on that other work.
511 /// This API marks the current thread as working on activity 'activityID'. This API
512 /// should be used when the caller knows the thread's current activity (the one being
513 /// overwritten) has completed. Otherwise, callers should prefer the overload that
514 /// return the oldActivityThatWillContinue (below).
515 ///
516 /// All events created with the EventSource on this thread are also tagged with the
517 /// activity ID of the thread.
518 ///
519 /// It is common, and good practice after setting the thread to an activity to log an event
520 /// with a 'start' opcode to indicate that precise time/thread where the new activity
521 /// started.
522 /// </summary>
523 /// <param name="activityId">A Guid that represents the new activity with which to mark
524 /// the current thread</param>
525 public static void SetCurrentThreadActivityId(Guid activityId)
527 if (TplEventSource.Log != null)
528 TplEventSource.Log.SetActivityId(activityId);
530 // We ignore errors to keep with the convention that EventSources do not throw errors.
531 // Note we can't access m_throwOnWrites because this is a static method.
532 #if FEATURE_MANAGED_ETW
533 #if FEATURE_PERFTRACING
534 // Set the activity id via EventPipe.
535 EventPipeInternal.EventActivityIdControl(
536 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
537 ref activityId);
538 #endif // FEATURE_PERFTRACING
539 #if PLATFORM_WINDOWS
540 // Set the activity id via ETW.
541 Interop.Advapi32.EventActivityIdControl(
542 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
543 ref activityId);
544 #endif // PLATFORM_WINDOWS
545 #endif // FEATURE_MANAGED_ETW
548 /// <summary>
549 /// Retrieves the ETW activity ID associated with the current thread.
550 /// </summary>
551 public static Guid CurrentThreadActivityId
555 // We ignore errors to keep with the convention that EventSources do not throw
556 // errors. Note we can't access m_throwOnWrites because this is a static method.
557 Guid retVal = new Guid();
558 #if FEATURE_MANAGED_ETW
559 #if PLATFORM_WINDOWS
560 Interop.Advapi32.EventActivityIdControl(
561 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
562 ref retVal);
563 #elif FEATURE_PERFTRACING
564 EventPipeInternal.EventActivityIdControl(
565 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
566 ref retVal);
567 #endif // PLATFORM_WINDOWS
568 #endif // FEATURE_MANAGED_ETW
569 return retVal;
573 /// <summary>
574 /// When a thread starts work that is on behalf of 'something else' (typically another
575 /// thread or network request) it should mark the thread as working on that other work.
576 /// This API marks the current thread as working on activity 'activityID'. It returns
577 /// whatever activity the thread was previously marked with. There is a convention that
578 /// callers can assume that callees restore this activity mark before the callee returns.
579 /// To encourage this, this API returns the old activity, so that it can be restored later.
580 ///
581 /// All events created with the EventSource on this thread are also tagged with the
582 /// activity ID of the thread.
583 ///
584 /// It is common, and good practice after setting the thread to an activity to log an event
585 /// with a 'start' opcode to indicate that precise time/thread where the new activity
586 /// started.
587 /// </summary>
588 /// <param name="activityId">A Guid that represents the new activity with which to mark
589 /// the current thread</param>
590 /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
591 /// which will continue at some point in the future, on the current thread</param>
592 public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
594 oldActivityThatWillContinue = activityId;
595 #if FEATURE_MANAGED_ETW
596 // We ignore errors to keep with the convention that EventSources do not throw errors.
597 // Note we can't access m_throwOnWrites because this is a static method.
599 #if FEATURE_PERFTRACING && PLATFORM_WINDOWS
600 EventPipeInternal.EventActivityIdControl(
601 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
602 ref oldActivityThatWillContinue);
603 #elif FEATURE_PERFTRACING
604 EventPipeInternal.EventActivityIdControl(
605 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
606 ref oldActivityThatWillContinue);
607 #endif // FEATURE_PERFTRACING && PLATFORM_WINDOWS
609 #if PLATFORM_WINDOWS
610 Interop.Advapi32.EventActivityIdControl(
611 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
612 ref oldActivityThatWillContinue);
613 #endif // PLATFORM_WINDOWS
614 #endif // FEATURE_MANAGED_ETW
616 // We don't call the activityDying callback here because the caller has declared that
617 // it is not dying.
618 if (TplEventSource.Log != null)
619 TplEventSource.Log.SetActivityId(activityId);
621 #endregion
623 #region protected
624 /// <summary>
625 /// This is the constructor that most users will use to create their eventSource. It takes
626 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
627 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
628 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
629 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
630 /// the ETW provider name.
631 /// </summary>
632 protected EventSource()
633 : this(EventSourceSettings.EtwManifestEventFormat)
637 /// <summary>
638 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
639 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
640 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
641 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
642 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
643 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
644 ///
645 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
646 /// </summary>
647 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
648 protected EventSource(bool throwOnEventWriteErrors)
649 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
652 /// <summary>
653 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
654 /// </summary>
655 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
657 /// <summary>
658 /// Construct an EventSource with additional non-default settings.
659 ///
660 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
661 /// The first string is the key and the second is the value. These are not interpreted by EventSource
662 /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string).
663 /// </summary>
664 /// <param name="settings">See EventSourceSettings for more.</param>
665 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
666 protected EventSource(EventSourceSettings settings, params string[]? traits)
668 m_config = ValidateSettings(settings);
670 Guid eventSourceGuid;
671 string? eventSourceName;
673 EventMetadata[]? eventDescriptors;
674 byte[]? manifest;
675 GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
677 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
679 var myType = this.GetType();
680 eventSourceGuid = GetGuid(myType);
681 eventSourceName = GetName(myType);
684 Initialize(eventSourceGuid, eventSourceName, traits);
687 #if FEATURE_PERFTRACING
688 // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly
689 // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere.
690 private unsafe void DefineEventPipeEvents()
692 // If the EventSource is set to emit all events as TraceLogging events, skip this initialization.
693 // Events will be defined when they are emitted for the first time.
694 if(SelfDescribingEvents)
696 return;
699 Debug.Assert(m_eventData != null);
700 Debug.Assert(m_eventPipeProvider != null);
701 int cnt = m_eventData.Length;
702 for (int i = 0; i < cnt; i++)
704 uint eventID = (uint)m_eventData[i].Descriptor.EventId;
705 if (eventID == 0)
706 continue;
708 byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(m_eventData[i]);
709 uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
711 string eventName = m_eventData[i].Name;
712 long keywords = m_eventData[i].Descriptor.Keywords;
713 uint eventVersion = m_eventData[i].Descriptor.Version;
714 uint level = m_eventData[i].Descriptor.Level;
716 fixed (byte *pMetadata = metadata)
718 IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(
719 eventID,
720 eventName,
721 keywords,
722 eventVersion,
723 level,
724 pMetadata,
725 metadataLength);
727 Debug.Assert(eventHandle != IntPtr.Zero);
728 m_eventData[i].EventHandle = eventHandle;
732 #endif
734 internal virtual void GetMetadata(out Guid eventSourceGuid, out string? eventSourceName, out EventMetadata[]? eventData, out byte[]? manifestBytes)
737 // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
738 // On other architectures it is a no-op.
740 // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
741 // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
743 // 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.
745 eventSourceGuid = Guid.Empty;
746 eventSourceName = null;
747 eventData = null;
748 manifestBytes = null;
750 return;
753 /// <summary>
754 /// This method is called when the eventSource is updated by the controller.
755 /// </summary>
756 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
758 #pragma warning disable 1591
759 // optimized for common signatures (no args)
760 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
761 protected unsafe void WriteEvent(int eventId)
763 WriteEventCore(eventId, 0, null);
766 // optimized for common signatures (ints)
767 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
768 protected unsafe void WriteEvent(int eventId, int arg1)
770 if (m_eventSourceEnabled)
772 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
773 descrs[0].DataPointer = (IntPtr)(&arg1);
774 descrs[0].Size = 4;
775 descrs[0].Reserved = 0;
776 WriteEventCore(eventId, 1, descrs);
780 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
781 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
783 if (m_eventSourceEnabled)
785 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
786 descrs[0].DataPointer = (IntPtr)(&arg1);
787 descrs[0].Size = 4;
788 descrs[0].Reserved = 0;
789 descrs[1].DataPointer = (IntPtr)(&arg2);
790 descrs[1].Size = 4;
791 descrs[1].Reserved = 0;
792 WriteEventCore(eventId, 2, descrs);
796 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
797 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
799 if (m_eventSourceEnabled)
801 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
802 descrs[0].DataPointer = (IntPtr)(&arg1);
803 descrs[0].Size = 4;
804 descrs[0].Reserved = 0;
805 descrs[1].DataPointer = (IntPtr)(&arg2);
806 descrs[1].Size = 4;
807 descrs[1].Reserved = 0;
808 descrs[2].DataPointer = (IntPtr)(&arg3);
809 descrs[2].Size = 4;
810 descrs[2].Reserved = 0;
811 WriteEventCore(eventId, 3, descrs);
815 // optimized for common signatures (longs)
816 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
817 protected unsafe void WriteEvent(int eventId, long arg1)
819 if (m_eventSourceEnabled)
821 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
822 descrs[0].DataPointer = (IntPtr)(&arg1);
823 descrs[0].Size = 8;
824 descrs[0].Reserved = 0;
825 WriteEventCore(eventId, 1, descrs);
829 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
830 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
832 if (m_eventSourceEnabled)
834 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
835 descrs[0].DataPointer = (IntPtr)(&arg1);
836 descrs[0].Size = 8;
837 descrs[0].Reserved = 0;
838 descrs[1].DataPointer = (IntPtr)(&arg2);
839 descrs[1].Size = 8;
840 descrs[1].Reserved = 0;
841 WriteEventCore(eventId, 2, descrs);
845 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
846 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
848 if (m_eventSourceEnabled)
850 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
851 descrs[0].DataPointer = (IntPtr)(&arg1);
852 descrs[0].Size = 8;
853 descrs[0].Reserved = 0;
854 descrs[1].DataPointer = (IntPtr)(&arg2);
855 descrs[1].Size = 8;
856 descrs[1].Reserved = 0;
857 descrs[2].DataPointer = (IntPtr)(&arg3);
858 descrs[2].Size = 8;
859 descrs[2].Reserved = 0;
860 WriteEventCore(eventId, 3, descrs);
864 // optimized for common signatures (strings)
865 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
866 protected unsafe void WriteEvent(int eventId, string? arg1)
868 if (m_eventSourceEnabled)
870 if (arg1 == null) arg1 = "";
871 fixed (char* string1Bytes = arg1)
873 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
874 descrs[0].DataPointer = (IntPtr)string1Bytes;
875 descrs[0].Size = ((arg1.Length + 1) * 2);
876 descrs[0].Reserved = 0;
877 WriteEventCore(eventId, 1, descrs);
882 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
883 protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2)
885 if (m_eventSourceEnabled)
887 if (arg1 == null) arg1 = "";
888 if (arg2 == null) arg2 = "";
889 fixed (char* string1Bytes = arg1)
890 fixed (char* string2Bytes = arg2)
892 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
893 descrs[0].DataPointer = (IntPtr)string1Bytes;
894 descrs[0].Size = ((arg1.Length + 1) * 2);
895 descrs[0].Reserved = 0;
896 descrs[1].DataPointer = (IntPtr)string2Bytes;
897 descrs[1].Size = ((arg2.Length + 1) * 2);
898 descrs[1].Reserved = 0;
899 WriteEventCore(eventId, 2, descrs);
904 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
905 protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3)
907 if (m_eventSourceEnabled)
909 if (arg1 == null) arg1 = "";
910 if (arg2 == null) arg2 = "";
911 if (arg3 == null) arg3 = "";
912 fixed (char* string1Bytes = arg1)
913 fixed (char* string2Bytes = arg2)
914 fixed (char* string3Bytes = arg3)
916 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
917 descrs[0].DataPointer = (IntPtr)string1Bytes;
918 descrs[0].Size = ((arg1.Length + 1) * 2);
919 descrs[0].Reserved = 0;
920 descrs[1].DataPointer = (IntPtr)string2Bytes;
921 descrs[1].Size = ((arg2.Length + 1) * 2);
922 descrs[1].Reserved = 0;
923 descrs[2].DataPointer = (IntPtr)string3Bytes;
924 descrs[2].Size = ((arg3.Length + 1) * 2);
925 descrs[2].Reserved = 0;
926 WriteEventCore(eventId, 3, descrs);
931 // optimized for common signatures (string and ints)
932 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
933 protected unsafe void WriteEvent(int eventId, string? arg1, int arg2)
935 if (m_eventSourceEnabled)
937 if (arg1 == null) arg1 = "";
938 fixed (char* string1Bytes = arg1)
940 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
941 descrs[0].DataPointer = (IntPtr)string1Bytes;
942 descrs[0].Size = ((arg1.Length + 1) * 2);
943 descrs[0].Reserved = 0;
944 descrs[1].DataPointer = (IntPtr)(&arg2);
945 descrs[1].Size = 4;
946 descrs[1].Reserved = 0;
947 WriteEventCore(eventId, 2, descrs);
952 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
953 protected unsafe void WriteEvent(int eventId, string? arg1, int arg2, int arg3)
955 if (m_eventSourceEnabled)
957 if (arg1 == null) arg1 = "";
958 fixed (char* string1Bytes = arg1)
960 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
961 descrs[0].DataPointer = (IntPtr)string1Bytes;
962 descrs[0].Size = ((arg1.Length + 1) * 2);
963 descrs[0].Reserved = 0;
964 descrs[1].DataPointer = (IntPtr)(&arg2);
965 descrs[1].Size = 4;
966 descrs[1].Reserved = 0;
967 descrs[2].DataPointer = (IntPtr)(&arg3);
968 descrs[2].Size = 4;
969 descrs[2].Reserved = 0;
970 WriteEventCore(eventId, 3, descrs);
975 // optimized for common signatures (string and longs)
976 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
977 protected unsafe void WriteEvent(int eventId, string? arg1, long arg2)
979 if (m_eventSourceEnabled)
981 if (arg1 == null) arg1 = "";
982 fixed (char* string1Bytes = arg1)
984 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
985 descrs[0].DataPointer = (IntPtr)string1Bytes;
986 descrs[0].Size = ((arg1.Length + 1) * 2);
987 descrs[0].Reserved = 0;
988 descrs[1].DataPointer = (IntPtr)(&arg2);
989 descrs[1].Size = 8;
990 descrs[1].Reserved = 0;
991 WriteEventCore(eventId, 2, descrs);
996 // optimized for common signatures (long and string)
997 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
998 protected unsafe void WriteEvent(int eventId, long arg1, string? arg2)
1000 if (m_eventSourceEnabled)
1002 if (arg2 == null) arg2 = "";
1003 fixed (char* string2Bytes = arg2)
1005 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1006 descrs[0].DataPointer = (IntPtr)(&arg1);
1007 descrs[0].Size = 8;
1008 descrs[0].Reserved = 0;
1009 descrs[1].DataPointer = (IntPtr)string2Bytes;
1010 descrs[1].Size = ((arg2.Length + 1) * 2);
1011 descrs[1].Reserved = 0;
1012 WriteEventCore(eventId, 2, descrs);
1017 // optimized for common signatures (int and string)
1018 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1019 protected unsafe void WriteEvent(int eventId, int arg1, string? arg2)
1021 if (m_eventSourceEnabled)
1023 if (arg2 == null) arg2 = "";
1024 fixed (char* string2Bytes = arg2)
1026 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1027 descrs[0].DataPointer = (IntPtr)(&arg1);
1028 descrs[0].Size = 4;
1029 descrs[0].Reserved = 0;
1030 descrs[1].DataPointer = (IntPtr)string2Bytes;
1031 descrs[1].Size = ((arg2.Length + 1) * 2);
1032 descrs[1].Reserved = 0;
1033 WriteEventCore(eventId, 2, descrs);
1038 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1039 protected unsafe void WriteEvent(int eventId, byte[]? arg1)
1041 if (m_eventSourceEnabled)
1043 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1044 if (arg1 == null || arg1.Length == 0)
1046 int blobSize = 0;
1047 descrs[0].DataPointer = (IntPtr)(&blobSize);
1048 descrs[0].Size = 4;
1049 descrs[0].Reserved = 0;
1050 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
1051 descrs[1].Size = 0;
1052 descrs[1].Reserved = 0;
1053 WriteEventCore(eventId, 2, descrs);
1055 else
1057 int blobSize = arg1.Length;
1058 fixed (byte* blob = &arg1[0])
1060 descrs[0].DataPointer = (IntPtr)(&blobSize);
1061 descrs[0].Size = 4;
1062 descrs[0].Reserved = 0;
1063 descrs[1].DataPointer = (IntPtr)blob;
1064 descrs[1].Size = blobSize;
1065 descrs[1].Reserved = 0;
1066 WriteEventCore(eventId, 2, descrs);
1072 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1073 protected unsafe void WriteEvent(int eventId, long arg1, byte[]? arg2)
1075 if (m_eventSourceEnabled)
1077 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1078 descrs[0].DataPointer = (IntPtr)(&arg1);
1079 descrs[0].Size = 8;
1080 descrs[0].Reserved = 0;
1081 if (arg2 == null || arg2.Length == 0)
1083 int blobSize = 0;
1084 descrs[1].DataPointer = (IntPtr)(&blobSize);
1085 descrs[1].Size = 4;
1086 descrs[1].Reserved = 0;
1087 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1088 descrs[2].Size = 0;
1089 descrs[2].Reserved = 0;
1090 WriteEventCore(eventId, 3, descrs);
1092 else
1094 int blobSize = arg2.Length;
1095 fixed (byte* blob = &arg2[0])
1097 descrs[1].DataPointer = (IntPtr)(&blobSize);
1098 descrs[1].Size = 4;
1099 descrs[1].Reserved = 0;
1100 descrs[2].DataPointer = (IntPtr)blob;
1101 descrs[2].Size = blobSize;
1102 descrs[2].Reserved = 0;
1103 WriteEventCore(eventId, 3, descrs);
1109 #pragma warning restore 1591
1111 /// <summary>
1112 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1113 /// </summary>
1114 protected internal struct EventData
1116 /// <summary>
1117 /// Address where the one argument lives (if this points to managed memory you must ensure the
1118 /// managed object is pinned.
1119 /// </summary>
1120 public unsafe IntPtr DataPointer { get { return (IntPtr)(void*)m_Ptr; } set { m_Ptr = unchecked((ulong)(void*)value); } }
1122 /// <summary>
1123 /// Size of the argument referenced by DataPointer
1124 /// </summary>
1125 public int Size { get { return m_Size; } set { m_Size = value; } }
1127 /// <summary>
1128 /// Reserved by ETW. This property is present to ensure that we can zero it
1129 /// since System.Private.CoreLib uses are not zero'd.
1130 /// </summary>
1131 internal int Reserved { get { return m_Reserved; } set { m_Reserved = value; } }
1133 #region private
1134 /// <summary>
1135 /// Initializes the members of this EventData object to point at a previously-pinned
1136 /// tracelogging-compatible metadata blob.
1137 /// </summary>
1138 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1139 /// <param name="size">The size of the metadata blob.</param>
1140 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1141 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1143 this.m_Ptr = (ulong)pointer;
1144 this.m_Size = size;
1145 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1148 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1149 // the way EventWrite wants it.
1150 internal ulong m_Ptr;
1151 internal int m_Size;
1152 #pragma warning disable 0649
1153 internal int m_Reserved; // Used to pad the size to match the Win32 API
1154 #pragma warning restore 0649
1155 #endregion
1158 /// <summary>
1159 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1160 /// do this, while straightforward, is unsafe.
1161 /// </summary>
1162 /// <remarks>
1163 /// <code>
1164 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1165 /// {
1166 /// if (IsEnabled())
1167 /// {
1168 /// if (arg2 == null) arg2 = "";
1169 /// fixed (char* string2Bytes = arg2)
1170 /// {
1171 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1172 /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1173 /// descrs[0].Size = 8;
1174 /// descrs[0].Reserved = 0;
1175 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1176 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1177 /// descrs[1].Reserved = 0;
1178 /// WriteEventCore(eventId, 2, descrs);
1179 /// }
1180 /// }
1181 /// }
1182 /// </code>
1183 /// </remarks>
1184 [CLSCompliant(false)]
1185 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1187 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1190 /// <summary>
1191 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1192 /// that you use to do this, while straightforward, is unsafe. The only difference from
1193 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1194 /// </summary>
1195 /// <remarks>
1196 /// <code>
1197 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1198 /// {
1199 /// if (IsEnabled())
1200 /// {
1201 /// if (arg2 == null) arg2 = "";
1202 /// fixed (char* string2Bytes = arg2)
1203 /// {
1204 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1205 /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1206 /// descrs[0].Size = 8;
1207 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1208 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1209 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1210 /// }
1211 /// }
1212 /// }
1213 /// </code>
1214 /// </remarks>
1215 [CLSCompliant(false)]
1216 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1218 if (m_eventSourceEnabled)
1220 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1223 if (relatedActivityId != null)
1224 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1226 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1227 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1228 Guid* pActivityId = null;
1229 Guid activityId = Guid.Empty;
1230 Guid relActivityId = Guid.Empty;
1232 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1233 ((activityOptions & EventActivityOptions.Disable) == 0))
1235 if (opcode == EventOpcode.Start)
1237 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1239 else if (opcode == EventOpcode.Stop)
1241 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1244 if (activityId != Guid.Empty)
1245 pActivityId = &activityId;
1246 if (relActivityId != Guid.Empty)
1247 relatedActivityId = &relActivityId;
1250 #if FEATURE_MANAGED_ETW
1251 if (m_eventData[eventId].EnabledForETW
1252 #if FEATURE_PERFTRACING
1253 || m_eventData[eventId].EnabledForEventPipe
1254 #endif // FEATURE_PERFTRACING
1257 if (!SelfDescribingEvents)
1259 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1260 ThrowEventSourceException(m_eventData[eventId].Name);
1261 #if FEATURE_PERFTRACING
1262 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1263 ThrowEventSourceException(m_eventData[eventId].Name);
1264 #endif // FEATURE_PERFTRACING
1266 else
1268 TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes;
1269 if (tlet == null)
1271 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1272 m_eventData[eventId].Tags,
1273 m_eventData[eventId].Parameters);
1274 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1277 EventSourceOptions opt = new EventSourceOptions
1279 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1280 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1281 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1284 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1287 #endif // FEATURE_MANAGED_ETW
1289 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1290 WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data);
1292 catch (Exception ex)
1294 if (ex is EventSourceException)
1295 throw;
1296 else
1297 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1302 // fallback varags helpers.
1303 /// <summary>
1304 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1305 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1306 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1307 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1308 /// check so that the varargs call is not made when the EventSource is not active.
1309 /// </summary>
1310 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1311 protected unsafe void WriteEvent(int eventId, params object?[] args)
1313 WriteEventVarargs(eventId, null, args);
1316 /// <summary>
1317 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1318 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1319 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1320 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1321 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1322 /// check so that the varargs call is not made when the EventSource is not active.
1323 /// </summary>
1324 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object?[] args)
1326 WriteEventVarargs(eventId, &relatedActivityId, args);
1329 #endregion
1331 #region IDisposable Members
1332 /// <summary>
1333 /// Disposes of an EventSource.
1334 /// </summary>
1335 public void Dispose()
1337 this.Dispose(true);
1338 GC.SuppressFinalize(this);
1340 /// <summary>
1341 /// Disposes of an EventSource.
1342 /// </summary>
1343 /// <remarks>
1344 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1345 /// Guidelines:
1346 /// 1. We may be called more than once: do nothing after the first call.
1347 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1348 /// </remarks>
1349 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1350 protected virtual void Dispose(bool disposing)
1352 if (disposing)
1354 #if FEATURE_MANAGED_ETW
1355 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1356 // even in scenarios with a high volume of ETW events.
1357 if (m_eventSourceEnabled)
1361 SendManifest(m_rawManifest);
1363 catch (Exception)
1364 { } // If it fails, simply give up.
1365 m_eventSourceEnabled = false;
1367 if (m_etwProvider != null)
1369 m_etwProvider.Dispose();
1370 m_etwProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose
1372 #endif
1373 #if FEATURE_PERFTRACING
1374 if (m_eventPipeProvider != null)
1376 m_eventPipeProvider.Dispose();
1377 m_eventPipeProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose
1379 #endif
1381 m_eventSourceEnabled = false;
1382 m_eventSourceDisposed = true;
1384 /// <summary>
1385 /// Finalizer for EventSource
1386 /// </summary>
1387 ~EventSource()
1389 this.Dispose(false);
1391 #endregion
1393 #region private
1395 private unsafe void WriteEventRaw(
1396 string? eventName,
1397 ref EventDescriptor eventDescriptor,
1398 IntPtr eventHandle,
1399 Guid* activityID,
1400 Guid* relatedActivityID,
1401 int dataCount,
1402 IntPtr data)
1404 #if FEATURE_MANAGED_ETW
1405 if (m_etwProvider == null)
1407 ThrowEventSourceException(eventName);
1409 else
1411 if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1412 ThrowEventSourceException(eventName);
1414 #endif // FEATURE_MANAGED_ETW
1415 #if FEATURE_PERFTRACING
1416 if (m_eventPipeProvider == null)
1418 ThrowEventSourceException(eventName);
1420 else
1422 if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1423 ThrowEventSourceException(eventName);
1425 #endif // FEATURE_PERFTRACING
1428 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1429 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1430 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1431 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1434 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1435 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[]? traits = null)
1437 m_config = ValidateSettings(settings);
1438 Initialize(eventSourceGuid, eventSourceName, traits);
1441 /// <summary>
1442 /// This method is responsible for the common initialization path from our constructors. It must
1443 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1444 /// "Log", such an exception would become a cached exception for the initialization of the static
1445 /// member, and any future access to the "Log" would throw the cached exception).
1446 /// </summary>
1447 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1448 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[]? traits)
1452 m_traits = traits;
1453 if (m_traits != null && m_traits.Length % 2 != 0)
1455 throw new ArgumentException(SR.EventSource_TraitEven, nameof(traits));
1458 if (eventSourceGuid == Guid.Empty)
1460 throw new ArgumentException(SR.EventSource_NeedGuid);
1463 if (eventSourceName == null)
1465 throw new ArgumentException(SR.EventSource_NeedName);
1468 m_name = eventSourceName;
1469 m_guid = eventSourceGuid;
1471 //Enable Implicit Activity tracker
1472 m_activityTracker = ActivityTracker.Instance;
1474 #if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
1475 // Create and register our provider traits. We do this early because it is needed to log errors
1476 // In the self-describing event case.
1477 this.InitializeProviderMetadata();
1478 #endif
1479 #if FEATURE_MANAGED_ETW
1480 // Register the provider with ETW
1481 var etwProvider = new OverideEventProvider(this, EventProviderType.ETW);
1482 etwProvider.Register(this);
1483 #endif
1484 #if FEATURE_PERFTRACING
1485 // Register the provider with EventPipe
1486 var eventPipeProvider = new OverideEventProvider(this, EventProviderType.EventPipe);
1487 lock (EventListener.EventListenersLock)
1489 eventPipeProvider.Register(this);
1491 #endif
1492 // Add the eventSource to the global (weak) list.
1493 // This also sets m_id, which is the index in the list.
1494 EventListener.AddEventSource(this);
1496 #if FEATURE_MANAGED_ETW
1497 // OK if we get this far without an exception, then we can at least write out error messages.
1498 // Set m_provider, which allows this.
1499 m_etwProvider = etwProvider;
1501 #if PLATFORM_WINDOWS
1502 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1503 // API available on OS >= Win 8 and patched Win 7.
1504 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1505 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1506 #endif
1508 int setInformationResult;
1509 System.Runtime.InteropServices.GCHandle metadataHandle =
1510 System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1511 IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1513 setInformationResult = m_etwProvider.SetInformation(
1514 Interop.Advapi32.EVENT_INFO_CLASS.SetTraits,
1515 providerMetadata,
1516 (uint)this.providerMetadata.Length);
1518 metadataHandle.Free();
1520 #endif // PLATFORM_WINDOWS
1521 #endif // FEATURE_MANAGED_ETW
1523 #if FEATURE_PERFTRACING
1524 m_eventPipeProvider = eventPipeProvider;
1525 #endif
1526 Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1527 // We are logically completely initialized at this point.
1528 m_completelyInited = true;
1530 catch (Exception e)
1532 if (m_constructionException == null)
1533 m_constructionException = e;
1534 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1537 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1538 lock (EventListener.EventListenersLock)
1540 // If there are any deferred commands, we can do them now.
1541 // This is the most likely place for exceptions to happen.
1542 // Note that we are NOT resetting m_deferredCommands to NULL here,
1543 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1544 EventCommandEventArgs? deferredCommands = m_deferredCommands;
1545 while (deferredCommands != null)
1547 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1548 deferredCommands = deferredCommands.nextCommand;
1553 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1555 if (eventSourceType == null)
1556 throw new ArgumentNullException(nameof(eventSourceType));
1558 EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1559 if (attrib != null && attrib.Name != null)
1560 return attrib.Name;
1562 return eventSourceType.Name;
1565 /// <summary>
1566 /// Implements the SHA1 hashing algorithm. Note that this
1567 /// implementation is for hashing public information. Do not
1568 /// use this code to hash private data, as this implementation does
1569 /// not take any steps to avoid information disclosure.
1570 /// </summary>
1571 private struct Sha1ForNonSecretPurposes
1573 private long length; // Total message length in bits
1574 private uint[] w; // Workspace
1575 private int pos; // Length of current chunk in bytes
1577 /// <summary>
1578 /// Call Start() to initialize the hash object.
1579 /// </summary>
1580 public void Start()
1582 if (this.w == null)
1584 this.w = new uint[85];
1587 this.length = 0;
1588 this.pos = 0;
1589 this.w[80] = 0x67452301;
1590 this.w[81] = 0xEFCDAB89;
1591 this.w[82] = 0x98BADCFE;
1592 this.w[83] = 0x10325476;
1593 this.w[84] = 0xC3D2E1F0;
1596 /// <summary>
1597 /// Adds an input byte to the hash.
1598 /// </summary>
1599 /// <param name="input">Data to include in the hash.</param>
1600 public void Append(byte input)
1602 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1603 if (64 == ++this.pos)
1605 this.Drain();
1609 /// <summary>
1610 /// Adds input bytes to the hash.
1611 /// </summary>
1612 /// <param name="input">
1613 /// Data to include in the hash. Must not be null.
1614 /// </param>
1615 public void Append(byte[] input)
1617 foreach (var b in input)
1619 this.Append(b);
1623 /// <summary>
1624 /// Retrieves the hash value.
1625 /// Note that after calling this function, the hash object should
1626 /// be considered uninitialized. Subsequent calls to Append or
1627 /// Finish will produce useless results. Call Start() to
1628 /// reinitialize.
1629 /// </summary>
1630 /// <param name="output">
1631 /// Buffer to receive the hash value. Must not be null.
1632 /// Up to 20 bytes of hash will be written to the output buffer.
1633 /// If the buffer is smaller than 20 bytes, the remaining hash
1634 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1635 /// rest of the buffer is left unmodified.
1636 /// </param>
1637 public void Finish(byte[] output)
1639 long l = this.length + 8 * this.pos;
1640 this.Append(0x80);
1641 while (this.pos != 56)
1643 this.Append(0x00);
1646 unchecked
1648 this.Append((byte)(l >> 56));
1649 this.Append((byte)(l >> 48));
1650 this.Append((byte)(l >> 40));
1651 this.Append((byte)(l >> 32));
1652 this.Append((byte)(l >> 24));
1653 this.Append((byte)(l >> 16));
1654 this.Append((byte)(l >> 8));
1655 this.Append((byte)l);
1657 int end = output.Length < 20 ? output.Length : 20;
1658 for (int i = 0; i != end; i++)
1660 uint temp = this.w[80 + i / 4];
1661 output[i] = (byte)(temp >> 24);
1662 this.w[80 + i / 4] = temp << 8;
1667 /// <summary>
1668 /// Called when this.pos reaches 64.
1669 /// </summary>
1670 private void Drain()
1672 for (int i = 16; i != 80; i++)
1674 this.w[i] = BitOperations.RotateLeft((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]), 1);
1677 unchecked
1679 uint a = this.w[80];
1680 uint b = this.w[81];
1681 uint c = this.w[82];
1682 uint d = this.w[83];
1683 uint e = this.w[84];
1685 for (int i = 0; i != 20; i++)
1687 const uint k = 0x5A827999;
1688 uint f = (b & c) | ((~b) & d);
1689 uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
1692 for (int i = 20; i != 40; i++)
1694 uint f = b ^ c ^ d;
1695 const uint k = 0x6ED9EBA1;
1696 uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
1699 for (int i = 40; i != 60; i++)
1701 uint f = (b & c) | (b & d) | (c & d);
1702 const uint k = 0x8F1BBCDC;
1703 uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
1706 for (int i = 60; i != 80; i++)
1708 uint f = b ^ c ^ d;
1709 const uint k = 0xCA62C1D6;
1710 uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
1713 this.w[80] += a;
1714 this.w[81] += b;
1715 this.w[82] += c;
1716 this.w[83] += d;
1717 this.w[84] += e;
1720 this.length += 512; // 64 bytes == 512 bits
1721 this.pos = 0;
1725 private static Guid GenerateGuidFromName(string name)
1727 if (namespaceBytes == null)
1729 namespaceBytes = new byte[] {
1730 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
1731 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
1735 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1736 var hash = new Sha1ForNonSecretPurposes();
1737 hash.Start();
1738 hash.Append(namespaceBytes);
1739 hash.Append(bytes);
1740 Array.Resize(ref bytes, 16);
1741 hash.Finish(bytes);
1743 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1744 return new Guid(bytes);
1747 private unsafe object? DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1749 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1750 // the recursion, but can we do this in a robust way?
1752 IntPtr dataPointer = data->DataPointer;
1753 // advance to next EventData in array
1754 ++data;
1756 Debug.Assert(m_eventData != null);
1757 Type dataType = GetDataType(m_eventData[eventId], parameterId);
1759 Again:
1760 if (dataType == typeof(IntPtr))
1762 return *((IntPtr*)dataPointer);
1764 else if (dataType == typeof(int))
1766 return *((int*)dataPointer);
1768 else if (dataType == typeof(uint))
1770 return *((uint*)dataPointer);
1772 else if (dataType == typeof(long))
1774 return *((long*)dataPointer);
1776 else if (dataType == typeof(ulong))
1778 return *((ulong*)dataPointer);
1780 else if (dataType == typeof(byte))
1782 return *((byte*)dataPointer);
1784 else if (dataType == typeof(sbyte))
1786 return *((sbyte*)dataPointer);
1788 else if (dataType == typeof(short))
1790 return *((short*)dataPointer);
1792 else if (dataType == typeof(ushort))
1794 return *((ushort*)dataPointer);
1796 else if (dataType == typeof(float))
1798 return *((float*)dataPointer);
1800 else if (dataType == typeof(double))
1802 return *((double*)dataPointer);
1804 else if (dataType == typeof(decimal))
1806 return *((decimal*)dataPointer);
1808 else if (dataType == typeof(bool))
1810 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1811 if (*((int*)dataPointer) == 1)
1813 return true;
1815 else
1817 return false;
1820 else if (dataType == typeof(Guid))
1822 return *((Guid*)dataPointer);
1824 else if (dataType == typeof(char))
1826 return *((char*)dataPointer);
1828 else if (dataType == typeof(DateTime))
1830 long dateTimeTicks = *((long*)dataPointer);
1831 return DateTime.FromFileTimeUtc(dateTimeTicks);
1833 else if (dataType == typeof(byte[]))
1835 // byte[] are written to EventData* as an int followed by a blob
1836 int cbSize = *((int*)dataPointer);
1837 byte[] blob = new byte[cbSize];
1838 dataPointer = data->DataPointer;
1839 data++;
1840 for (int i = 0; i < cbSize; ++i)
1841 blob[i] = *((byte*)(dataPointer + i));
1842 return blob;
1844 else if (dataType == typeof(byte*))
1846 // TODO: how do we want to handle this? For now we ignore it...
1847 return null;
1849 else
1851 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1853 return null;
1858 m_EventSourceInDecodeObject = true;
1860 if (dataType.IsEnum())
1862 dataType = Enum.GetUnderlyingType(dataType);
1863 #if ES_BUILD_PN
1864 int dataTypeSize = (int)dataType.TypeHandle.ToEETypePtr().ValueTypeSize;
1865 #else
1866 int dataTypeSize = System.Runtime.InteropServices.Marshal.SizeOf(dataType);
1867 #endif
1868 if (dataTypeSize < sizeof(int))
1869 dataType = typeof(int);
1870 goto Again;
1873 // Everything else is marshaled as a string.
1874 // ETW strings are NULL-terminated, so marshal everything up to the first
1875 // null in the string.
1876 //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1877 if(dataPointer == IntPtr.Zero)
1879 return null;
1882 return new string((char *)dataPointer);
1884 finally
1886 m_EventSourceInDecodeObject = false;
1891 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1892 // eventSource).
1893 private EventDispatcher? GetDispatcher(EventListener? listener)
1895 EventDispatcher? dispatcher = m_Dispatchers;
1896 while (dispatcher != null)
1898 if (dispatcher.m_Listener == listener)
1899 return dispatcher;
1900 dispatcher = dispatcher.m_Next;
1902 return dispatcher;
1905 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object?[] args)
1907 if (m_eventSourceEnabled)
1909 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1912 if (childActivityID != null)
1914 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1916 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1917 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1918 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1919 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1920 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1921 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1922 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1923 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1924 if (!m_eventData[eventId].HasRelatedActivityID)
1926 throw new ArgumentException(SR.EventSource_NoRelatedActivityId);
1930 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1932 Guid* pActivityId = null;
1933 Guid activityId = Guid.Empty;
1934 Guid relatedActivityId = Guid.Empty;
1935 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1936 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1938 if (childActivityID == null &&
1939 ((activityOptions & EventActivityOptions.Disable) == 0))
1941 if (opcode == EventOpcode.Start)
1943 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1945 else if (opcode == EventOpcode.Stop)
1947 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1950 if (activityId != Guid.Empty)
1951 pActivityId = &activityId;
1952 if (relatedActivityId != Guid.Empty)
1953 childActivityID = &relatedActivityId;
1956 #if FEATURE_MANAGED_ETW
1957 if (m_eventData[eventId].EnabledForETW
1958 #if FEATURE_PERFTRACING
1959 || m_eventData[eventId].EnabledForEventPipe
1960 #endif // FEATURE_PERFTRACING
1963 if (!SelfDescribingEvents)
1965 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1966 ThrowEventSourceException(m_eventData[eventId].Name);
1967 #if FEATURE_PERFTRACING
1968 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1969 ThrowEventSourceException(m_eventData[eventId].Name);
1970 #endif // FEATURE_PERFTRACING
1972 else
1974 TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes;
1975 if (tlet == null)
1977 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1978 EventTags.None,
1979 m_eventData[eventId].Parameters);
1980 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1983 // TODO: activity ID support
1984 EventSourceOptions opt = new EventSourceOptions
1986 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1987 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1988 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1991 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1994 #endif // FEATURE_MANAGED_ETW
1995 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1997 #if !ES_BUILD_STANDALONE
1998 // Maintain old behavior - object identity is preserved
1999 if (LocalAppContextSwitches.PreserveEventListnerObjectIdentity)
2001 WriteToAllListeners(
2002 eventId: eventId,
2003 osThreadId: null,
2004 timeStamp: null,
2005 activityID: pActivityId,
2006 childActivityID: childActivityID,
2007 args: args);
2009 else
2010 #endif // !ES_BUILD_STANDALONE
2012 object?[] serializedArgs = SerializeEventArgs(eventId, args);
2013 WriteToAllListeners(
2014 eventId: eventId,
2015 osThreadId: null,
2016 timeStamp: null,
2017 activityID: pActivityId,
2018 childActivityID: childActivityID,
2019 args: serializedArgs);
2023 catch (Exception ex)
2025 if (ex is EventSourceException)
2026 throw;
2027 else
2028 ThrowEventSourceException(m_eventData[eventId].Name, ex);
2033 private unsafe object?[] SerializeEventArgs(int eventId, object?[] args)
2035 Debug.Assert(m_eventData != null);
2036 TraceLoggingEventTypes? eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2037 if (eventTypes == null)
2039 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2040 EventTags.None,
2041 m_eventData[eventId].Parameters);
2042 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2044 var eventData = new object?[eventTypes.typeInfos.Length];
2045 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2047 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2049 return eventData;
2052 /// <summary>
2053 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2054 /// checks that they in fact match and logs a warning to the debugger if they don't.
2055 /// </summary>
2056 /// <param name="infos"></param>
2057 /// <param name="args"></param>
2058 private void LogEventArgsMismatches(ParameterInfo[] infos, object?[] args)
2060 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2061 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2062 // writing to the debugger log on PCL.
2063 bool typesMatch = args.Length == infos.Length;
2065 int i = 0;
2066 while (typesMatch && i < args.Length)
2068 Type pType = infos[i].ParameterType;
2070 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2071 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
2072 // argument is null and the parameter type is non-nullable.
2073 if ((args[i] != null && (args[i]!.GetType() != pType)) // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
2074 || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
2077 typesMatch = false;
2078 break;
2081 ++i;
2084 if (!typesMatch)
2086 System.Diagnostics.Debugger.Log(0, null, SR.EventSource_VarArgsParameterMismatch + "\r\n");
2088 #endif //!ES_BUILD_PCL
2091 private unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2093 Debug.Assert(m_eventData != null);
2094 // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
2095 // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
2096 // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
2097 // we just used the modifiedParamCount here -- so we need both.
2098 int paramCount = GetParameterCount(m_eventData[eventId]);
2099 int modifiedParamCount = 0;
2100 for (int i = 0; i < paramCount; i++)
2102 Type parameterType = GetDataType(m_eventData[eventId], i);
2103 if (parameterType == typeof(byte[]))
2105 modifiedParamCount += 2;
2107 else
2109 modifiedParamCount++;
2112 if (eventDataCount != modifiedParamCount)
2114 ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, eventDataCount, paramCount), true);
2115 paramCount = Math.Min(paramCount, eventDataCount);
2118 object?[] args = new object[paramCount];
2120 EventSource.EventData* dataPtr = data;
2121 for (int i = 0; i < paramCount; i++)
2122 args[i] = DecodeObject(eventId, i, ref dataPtr);
2123 WriteToAllListeners(
2124 eventId: eventId,
2125 osThreadId: null,
2126 timeStamp: null,
2127 activityID: activityID,
2128 childActivityID: childActivityID,
2129 args: args);
2132 // helper for writing to all EventListeners attached the current eventSource.
2133 internal unsafe void WriteToAllListeners(int eventId, uint* osThreadId, DateTime* timeStamp, Guid* activityID, Guid* childActivityID, params object?[] args)
2135 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2136 eventCallbackArgs.EventId = eventId;
2137 if (osThreadId != null)
2138 eventCallbackArgs.OSThreadId = (int)*osThreadId;
2139 if (timeStamp != null)
2140 eventCallbackArgs.TimeStamp = *timeStamp;
2141 if (activityID != null)
2142 eventCallbackArgs.ActivityId = *activityID;
2143 if (childActivityID != null)
2144 eventCallbackArgs.RelatedActivityId = *childActivityID;
2146 Debug.Assert(m_eventData != null);
2147 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2148 eventCallbackArgs.Message = m_eventData[eventId].Message;
2149 eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(args);
2151 DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2154 private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2156 Exception? lastThrownException = null;
2157 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2159 Debug.Assert(dispatcher.m_EventEnabled != null);
2160 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2165 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2167 catch (Exception e)
2169 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2170 + e.Message, false);
2171 lastThrownException = e;
2177 if (lastThrownException != null)
2179 throw new EventSourceException(lastThrownException);
2183 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2184 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2186 #if FEATURE_MANAGED_ETW
2187 if (m_etwProvider != null)
2189 string eventName = "EventSourceMessage";
2190 if (SelfDescribingEvents)
2192 EventSourceOptions opt = new EventSourceOptions
2194 Keywords = (EventKeywords)unchecked(keywords),
2195 Level = level
2197 var msg = new { message = msgString };
2198 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2199 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2201 else
2203 // We want the name of the provider to show up so if we don't have a manifest we create
2204 // on that at least has the provider name (I don't define any events).
2205 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2207 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2208 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2209 manifestBuilder.AddEventParameter(typeof(string), "message");
2210 manifestBuilder.EndEvent();
2211 SendManifest(manifestBuilder.CreateManifest());
2214 // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited.
2215 fixed (char* msgStringPtr = msgString)
2217 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2218 EventProvider.EventData data = new EventProvider.EventData();
2219 data.Ptr = (ulong)msgStringPtr;
2220 data.Size = (uint)(2 * (msgString.Length + 1));
2221 data.Reserved = 0;
2222 m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
2226 #endif // FEATURE_MANAGED_ETW
2229 /// <summary>
2230 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2231 /// while writing the message to any one of the listeners will be silently ignored.
2232 /// </summary>
2233 private void WriteStringToAllListeners(string eventName, string msg)
2235 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2236 eventCallbackArgs.EventId = 0;
2237 eventCallbackArgs.Message = msg;
2238 eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(new List<object?>() { msg });
2239 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2240 eventCallbackArgs.EventName = eventName;
2242 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2244 bool dispatcherEnabled = false;
2245 if (dispatcher.m_EventEnabled == null)
2247 // if the listeners that weren't correctly initialized, we will send to it
2248 // since this is an error message and we want to see it go out.
2249 dispatcherEnabled = true;
2251 else
2253 // if there's *any* enabled event on the dispatcher we'll write out the string
2254 // otherwise we'll treat the listener as disabled and skip it
2255 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2257 if (dispatcher.m_EventEnabled[evtId])
2259 dispatcherEnabled = true;
2260 break;
2266 if (dispatcherEnabled)
2267 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2269 catch
2271 // ignore any exceptions thrown by listeners' OnEventWritten
2276 /// <summary>
2277 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2278 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2279 /// </summary>
2280 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2282 if (!enable)
2283 return false;
2285 Debug.Assert(m_eventData != null);
2286 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2287 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2289 #if FEATURE_MANAGED_ETW_CHANNELS
2290 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2291 #else
2292 EventChannel channel = EventChannel.None;
2293 #endif
2295 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2298 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2299 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2301 if (!enabled)
2302 return false;
2304 // does is pass the level test?
2305 if ((currentLevel != 0) && (currentLevel < eventLevel))
2306 return false;
2308 // if yes, does it pass the keywords test?
2309 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2311 #if FEATURE_MANAGED_ETW_CHANNELS
2312 // is there a channel with keywords that match currentMatchAnyKeyword?
2313 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2315 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2316 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2317 return false;
2319 else
2320 #endif
2322 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2323 return false;
2326 return true;
2329 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2330 private void ThrowEventSourceException(string? eventName, Exception? innerEx = null)
2332 // If we fail during out of band logging we may end up trying
2333 // to throw another EventSourceException, thus hitting a StackOverflowException.
2334 // Avoid StackOverflow by making sure we do not recursively call this method.
2335 if (m_EventSourceExceptionRecurenceCount > 0)
2336 return;
2339 m_EventSourceExceptionRecurenceCount++;
2341 string errorPrefix = "EventSourceException";
2342 if (eventName != null)
2344 errorPrefix += " while processing event \"" + eventName + "\"";
2347 // TODO Create variations of EventSourceException that indicate more information using the error code.
2348 switch (EventProvider.GetLastWriteEventError())
2350 case EventProvider.WriteEventErrorCode.EventTooBig:
2351 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_EventTooBig, true);
2352 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_EventTooBig, innerEx);
2353 break;
2354 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2355 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NoFreeBuffers, true);
2356 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NoFreeBuffers, innerEx);
2357 break;
2358 case EventProvider.WriteEventErrorCode.NullInput:
2359 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NullInput, true);
2360 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NullInput, innerEx);
2361 break;
2362 case EventProvider.WriteEventErrorCode.TooManyArgs:
2363 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_TooManyArgs, true);
2364 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_TooManyArgs, innerEx);
2365 break;
2366 default:
2367 if (innerEx != null)
2369 innerEx = innerEx.GetBaseException();
2370 ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2372 else
2373 ReportOutOfBandMessage(errorPrefix, true);
2374 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2375 break;
2378 finally
2380 m_EventSourceExceptionRecurenceCount--;
2384 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string? eventName)
2386 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2387 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2388 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2390 ThrowEventSourceException(eventName);
2394 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eventName)
2396 if (opcode == EventOpcode.Info && eventName != null)
2398 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2400 return EventOpcode.Start;
2402 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2404 return EventOpcode.Stop;
2408 return opcode;
2411 #if FEATURE_MANAGED_ETW
2412 /// <summary>
2413 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2414 /// </summary>
2415 private class OverideEventProvider : EventProvider
2417 public OverideEventProvider(EventSource eventSource, EventProviderType providerType)
2418 : base(providerType)
2420 this.m_eventSource = eventSource;
2421 this.m_eventProviderType = providerType;
2423 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string?>? arguments,
2424 int perEventSourceSessionId, int etwSessionId)
2426 // We use null to represent the ETW EventListener.
2427 EventListener? listener = null;
2428 m_eventSource.SendCommand(listener, m_eventProviderType, perEventSourceSessionId, etwSessionId,
2429 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2431 private EventSource m_eventSource;
2432 private EventProviderType m_eventProviderType;
2434 #endif
2436 /// <summary>
2437 /// Used to hold all the static information about an event. This includes everything in the event
2438 /// descriptor as well as some stuff we added specifically for EventSource. see the
2439 /// code:m_eventData for where we use this.
2440 /// </summary>
2443 EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2444 now the move to CoreLib marked them as private.
2445 While they are technically private (it's a contract used between the library and the ILC toolchain),
2446 we need them to be rooted and exported from shared library for the system to work.
2447 For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2448 root them and modify shared library definition to force export them.
2450 #if ES_BUILD_PN
2451 public
2452 #else
2453 internal
2454 #endif
2455 partial struct EventMetadata
2457 #if ES_BUILD_PN
2458 public EventMetadata(EventDescriptor descriptor,
2459 EventTags tags,
2460 bool enabledForAnyListener,
2461 bool enabledForETW,
2462 string name,
2463 string message,
2464 EventParameterType[] parameterTypes)
2466 this.Descriptor = descriptor;
2467 this.Tags = tags;
2468 this.EnabledForAnyListener = enabledForAnyListener;
2469 this.EnabledForETW = enabledForETW;
2470 #if FEATURE_PERFTRACING
2471 this.EnabledForEventPipe = false;
2472 #endif
2473 this.TriggersActivityTracking = 0;
2474 this.Name = name;
2475 this.Message = message;
2476 this.Parameters = null!;
2477 this.TraceLoggingEventTypes = null;
2478 this.ActivityOptions = EventActivityOptions.None;
2479 this.ParameterTypes = parameterTypes;
2480 this.HasRelatedActivityID = false;
2481 this.EventHandle = IntPtr.Zero;
2483 #endif
2485 public EventDescriptor Descriptor;
2486 public IntPtr EventHandle; // EventPipeEvent handle.
2487 public EventTags Tags;
2488 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2489 public bool EnabledForETW; // is this event on for ETW?
2490 #if FEATURE_PERFTRACING
2491 public bool EnabledForEventPipe; // is this event on for EventPipe?
2492 #endif
2494 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2495 #pragma warning disable 0649
2496 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2497 #pragma warning restore 0649
2498 public string Name; // the name of the event
2499 public string? Message; // If the event has a message associated with it, this is it.
2500 public ParameterInfo[] Parameters; // TODO can we remove?
2502 public TraceLoggingEventTypes? TraceLoggingEventTypes;
2503 public EventActivityOptions ActivityOptions;
2505 #if ES_BUILD_PN
2506 public EventParameterType[] ParameterTypes;
2507 #endif
2510 #if !ES_BUILD_PN
2511 private int GetParameterCount(EventMetadata eventData)
2513 return eventData.Parameters.Length;
2516 private Type GetDataType(EventMetadata eventData, int parameterId)
2518 return eventData.Parameters[parameterId].ParameterType;
2521 private static readonly bool m_EventSourcePreventRecursion = false;
2522 #else
2523 private int GetParameterCount(EventMetadata eventData)
2525 int paramCount;
2526 if(eventData.Parameters == null)
2528 paramCount = eventData.ParameterTypes.Length;
2530 else
2532 paramCount = eventData.Parameters.Length;
2535 return paramCount;
2538 private Type GetDataType(EventMetadata eventData, int parameterId)
2540 Type dataType;
2541 if(eventData.Parameters == null)
2543 dataType = EventTypeToType(eventData.ParameterTypes[parameterId]);
2545 else
2547 dataType = eventData.Parameters[parameterId].ParameterType;
2550 return dataType;
2553 private static readonly bool m_EventSourcePreventRecursion = true;
2555 public enum EventParameterType
2557 Boolean,
2558 Byte,
2559 SByte,
2560 Char,
2561 Int16,
2562 UInt16,
2563 Int32,
2564 UInt32,
2565 Int64,
2566 UInt64,
2567 IntPtr,
2568 Single,
2569 Double,
2570 Decimal,
2571 Guid,
2572 String
2575 private Type EventTypeToType(EventParameterType type)
2577 switch (type)
2579 case EventParameterType.Boolean:
2580 return typeof(bool);
2581 case EventParameterType.Byte:
2582 return typeof(byte);
2583 case EventParameterType.SByte:
2584 return typeof(sbyte);
2585 case EventParameterType.Char:
2586 return typeof(char);
2587 case EventParameterType.Int16:
2588 return typeof(short);
2589 case EventParameterType.UInt16:
2590 return typeof(ushort);
2591 case EventParameterType.Int32:
2592 return typeof(int);
2593 case EventParameterType.UInt32:
2594 return typeof(uint);
2595 case EventParameterType.Int64:
2596 return typeof(long);
2597 case EventParameterType.UInt64:
2598 return typeof(ulong);
2599 case EventParameterType.IntPtr:
2600 return typeof(IntPtr);
2601 case EventParameterType.Single:
2602 return typeof(float);
2603 case EventParameterType.Double:
2604 return typeof(double);
2605 case EventParameterType.Decimal:
2606 return typeof(decimal);
2607 case EventParameterType.Guid:
2608 return typeof(Guid);
2609 case EventParameterType.String:
2610 return typeof(string);
2611 default:
2612 // TODO: should I throw an exception here?
2613 return null!;
2616 #endif
2618 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2619 // eventSource. The logic is as follows
2621 // * if Command == Update
2622 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2623 // to (if listener != null)
2624 // perEventSourceSessionId = 0 - reserved for EventListeners
2625 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2626 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2627 // Keywords that identifies the session
2628 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2629 // discriminated by etwSessionId
2630 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2631 // activity tracing across different providers (which might have different sessionIds
2632 // for the same ETW session)
2633 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2634 // eventSource. In particular, if 'enabled' is false, 'level' and
2635 // 'matchAnyKeywords' are not used.
2636 // * OnEventCommand is invoked, which may cause calls to
2637 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2638 // depending on the logic in that routine.
2639 // * else (command != Update)
2640 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2641 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2643 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2644 internal void SendCommand(EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId,
2645 EventCommand command, bool enable,
2646 EventLevel level, EventKeywords matchAnyKeyword,
2647 IDictionary<string, string?>? commandArguments)
2649 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, eventProviderType, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2650 lock (EventListener.EventListenersLock)
2652 if (m_completelyInited)
2654 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2655 this.m_deferredCommands = null;
2656 // We are fully initialized, do the command
2657 DoCommand(commandArgs);
2659 else
2661 // We can't do the command, simply remember it and we do it when we are fully constructed.
2662 if (m_deferredCommands == null)
2663 m_deferredCommands = commandArgs; // create the first entry
2664 else
2666 // We have one or more entries, find the last one and add it to that.
2667 EventCommandEventArgs lastCommand = m_deferredCommands;
2668 while (lastCommand.nextCommand != null)
2669 lastCommand = lastCommand.nextCommand;
2670 lastCommand.nextCommand = commandArgs;
2676 /// <summary>
2677 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2678 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2679 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2680 /// This helper actually does all actual command logic.
2681 /// </summary>
2682 internal void DoCommand(EventCommandEventArgs commandArgs)
2684 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2685 // We defer commands until we are completely inited. This allows error messages to be sent.
2686 Debug.Assert(m_completelyInited);
2688 #if FEATURE_MANAGED_ETW
2689 if (m_etwProvider == null) // If we failed to construct
2690 return;
2691 #endif // FEATURE_MANAGED_ETW
2692 #if FEATURE_PERFTRACING
2693 if (m_eventPipeProvider == null)
2694 return;
2695 #endif
2697 m_outOfBandMessageCount = 0;
2698 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2701 EnsureDescriptorsInitialized();
2702 Debug.Assert(m_eventData != null);
2704 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2705 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2706 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2708 throw new ArgumentException(SR.EventSource_ListenerNotFound);
2711 if (commandArgs.Arguments == null)
2712 commandArgs.Arguments = new Dictionary<string, string?>();
2714 if (commandArgs.Command == EventCommand.Update)
2716 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2717 for (int i = 0; i < m_eventData.Length; i++)
2718 EnableEventForDispatcher(commandArgs.dispatcher, commandArgs.eventProviderType, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2720 if (commandArgs.enable)
2722 if (!m_eventSourceEnabled)
2724 // EventSource turned on for the first time, simply copy the bits.
2725 m_level = commandArgs.level;
2726 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2728 else
2730 // Already enabled, make it the most verbose of the existing and new filter
2731 if (commandArgs.level > m_level)
2732 m_level = commandArgs.level;
2733 if (commandArgs.matchAnyKeyword == 0)
2734 m_matchAnyKeyword = 0;
2735 else if (m_matchAnyKeyword != 0)
2736 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2740 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2741 // represent 0-based positive values
2742 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2743 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2744 bSessionEnable = false;
2746 if (commandArgs.listener == null)
2748 if (!bSessionEnable)
2749 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2750 // for "global" enable/disable (passed in with listener == null and
2751 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2752 --commandArgs.perEventSourceSessionId;
2755 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2757 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2758 // hasn't changed.
2759 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2760 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2761 Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2763 // Send the manifest if we are enabling an ETW session
2764 if (bSessionEnable && commandArgs.dispatcher == null)
2766 // eventSourceDispatcher == null means this is the ETW manifest
2768 // Note that we unconditionally send the manifest whenever we are enabled, even if
2769 // we were already enabled. This is because there may be multiple sessions active
2770 // and we can't know that all the sessions have seen the manifest.
2771 if (!SelfDescribingEvents)
2772 SendManifest(m_rawManifest);
2775 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2776 // things like log messages, or test if keywords are enabled in the callback.
2777 if (commandArgs.enable)
2779 Debug.Assert(m_eventData != null);
2780 m_eventSourceEnabled = true;
2783 this.OnEventCommand(commandArgs);
2784 var eventCommandCallback = this.m_eventCommandExecuted;
2785 if (eventCommandCallback != null)
2786 eventCommandCallback(this, commandArgs);
2788 if (!commandArgs.enable)
2790 // If we are disabling, maybe we can turn on 'quick checks' to filter
2791 // quickly. These are all just optimizations (since later checks will still filter)
2793 // There is a good chance EnabledForAnyListener are not as accurate as
2794 // they could be, go ahead and get a better estimate.
2795 for (int i = 0; i < m_eventData.Length; i++)
2797 bool isEnabledForAnyListener = false;
2798 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2800 Debug.Assert(dispatcher.m_EventEnabled != null);
2802 if (dispatcher.m_EventEnabled[i])
2804 isEnabledForAnyListener = true;
2805 break;
2808 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2811 // If no events are enabled, disable the global enabled bit.
2812 if (!AnyEventEnabled())
2814 m_level = 0;
2815 m_matchAnyKeyword = 0;
2816 m_eventSourceEnabled = false;
2820 else
2822 #if !FEATURE_PERFTRACING
2823 if (commandArgs.Command == EventCommand.SendManifest)
2825 // TODO: should we generate the manifest here if we hadn't already?
2826 if (m_rawManifest != null)
2827 SendManifest(m_rawManifest);
2829 #endif
2831 // These are not used for non-update commands and thus should always be 'default' values
2832 // Debug.Assert(enable == true);
2833 // Debug.Assert(level == EventLevel.LogAlways);
2834 // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2836 this.OnEventCommand(commandArgs);
2837 var eventCommandCallback = m_eventCommandExecuted;
2838 if (eventCommandCallback != null)
2839 eventCommandCallback(this, commandArgs);
2842 catch (Exception e)
2844 // When the ETW session is created after the EventSource has registered with the ETW system
2845 // we can send any error messages here.
2846 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2847 // We never throw when doing a command.
2851 /// <summary>
2852 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2853 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2854 /// range return false, otherwise true.
2855 /// </summary>
2856 internal bool EnableEventForDispatcher(EventDispatcher? dispatcher, EventProviderType eventProviderType, int eventId, bool value)
2858 Debug.Assert(m_eventData != null);
2860 if (dispatcher == null)
2862 if (eventId >= m_eventData.Length)
2863 return false;
2864 #if FEATURE_MANAGED_ETW
2865 if (m_etwProvider != null && eventProviderType == EventProviderType.ETW)
2866 m_eventData[eventId].EnabledForETW = value;
2867 #endif
2868 #if FEATURE_PERFTRACING
2869 if (m_eventPipeProvider != null && eventProviderType == EventProviderType.EventPipe)
2870 m_eventData[eventId].EnabledForEventPipe = value;
2871 #endif
2873 else
2875 Debug.Assert(dispatcher.m_EventEnabled != null);
2876 if (eventId >= dispatcher.m_EventEnabled.Length)
2877 return false;
2878 dispatcher.m_EventEnabled[eventId] = value;
2879 if (value)
2880 m_eventData[eventId].EnabledForAnyListener = true;
2882 return true;
2885 /// <summary>
2886 /// Returns true if any event at all is on.
2887 /// </summary>
2888 private bool AnyEventEnabled()
2890 Debug.Assert(m_eventData != null);
2892 for (int i = 0; i < m_eventData.Length; i++)
2893 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener
2894 #if FEATURE_PERFTRACING
2895 || m_eventData[i].EnabledForEventPipe
2896 #endif // FEATURE_PERFTRACING
2898 return true;
2899 return false;
2902 private bool IsDisposed
2904 get { return m_eventSourceDisposed; }
2907 private void EnsureDescriptorsInitialized()
2909 #if !ES_BUILD_STANDALONE
2910 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
2911 #endif
2912 if (m_eventData == null)
2914 Guid eventSourceGuid = Guid.Empty;
2915 string? eventSourceName = null;
2916 EventMetadata[]? eventData = null;
2917 byte[]? manifest = null;
2919 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
2920 // to the reflection approach.
2921 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
2923 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
2925 // GetMetadata failed, so we have to set it via reflection.
2926 Debug.Assert(m_rawManifest == null);
2928 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
2929 Debug.Assert(m_eventData != null);
2931 else
2933 // GetMetadata worked, so set the fields as appropriate.
2934 m_name = eventSourceName;
2935 m_guid = eventSourceGuid;
2936 m_eventData = eventData;
2937 m_rawManifest = manifest;
2939 // TODO Enforce singleton pattern
2940 Debug.Assert(EventListener.s_EventSources != null, "should be called within lock on EventListener.EventListenersLock which ensures s_EventSources to be initialized");
2941 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
2943 if (eventSourceRef.Target is EventSource eventSource && eventSource.Guid == m_guid && !eventSource.IsDisposed)
2945 if (eventSource != this)
2947 throw new ArgumentException(SR.Format(SR.EventSource_EventSourceGuidInUse, m_guid));
2952 // Make certain all dispatchers also have their arrays initialized
2953 EventDispatcher? dispatcher = m_Dispatchers;
2954 while (dispatcher != null)
2956 if (dispatcher.m_EventEnabled == null)
2957 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
2958 dispatcher = dispatcher.m_Next;
2960 #if FEATURE_PERFTRACING
2961 // Initialize the EventPipe event handles.
2962 DefineEventPipeEvents();
2963 #endif
2965 if (s_currentPid == 0)
2967 #if ES_BUILD_STANDALONE
2968 // for non-BCL EventSource we must assert SecurityPermission
2969 new SecurityPermission(PermissionState.Unrestricted).Assert();
2970 #endif
2971 s_currentPid = Interop.GetCurrentProcessId();
2975 // Send out the ETW manifest XML out to ETW
2976 // Today, we only send the manifest to ETW, custom listeners don't get it.
2977 private unsafe bool SendManifest(byte[]? rawManifest)
2979 bool success = true;
2981 if (rawManifest == null)
2982 return false;
2984 Debug.Assert(!SelfDescribingEvents);
2986 #if FEATURE_MANAGED_ETW
2987 fixed (byte* dataPtr = rawManifest)
2989 // we don't want the manifest to show up in the event log channels so we specify as keywords
2990 // everything but the first 8 bits (reserved for the 8 channels)
2991 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
2992 ManifestEnvelope envelope = new ManifestEnvelope();
2994 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
2995 envelope.MajorVersion = 1;
2996 envelope.MinorVersion = 0;
2997 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
2998 int dataLeft = rawManifest.Length;
2999 envelope.ChunkNumber = 0;
3001 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
3003 dataDescrs[0].Ptr = (ulong)&envelope;
3004 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
3005 dataDescrs[0].Reserved = 0;
3007 dataDescrs[1].Ptr = (ulong)dataPtr;
3008 dataDescrs[1].Reserved = 0;
3010 int chunkSize = ManifestEnvelope.MaxChunkSize;
3011 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3012 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3013 while (dataLeft > 0)
3015 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3016 if (m_etwProvider != null)
3018 if (!m_etwProvider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
3020 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3021 // can fail. If we get this failure on the first chunk try again with something smaller
3022 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3023 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3025 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3027 chunkSize = chunkSize / 2;
3028 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3031 success = false;
3032 if (ThrowOnEventWriteErrors)
3033 ThrowEventSourceException("SendManifest");
3034 break;
3037 dataLeft -= chunkSize;
3038 dataDescrs[1].Ptr += (uint)chunkSize;
3039 envelope.ChunkNumber++;
3041 // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
3042 // 5 chunks, so only the largest manifests will hit the pause.
3043 if ((envelope.ChunkNumber % 5) == 0)
3045 Thread.Sleep(15);
3049 #endif // FEATURE_MANAGED_ETW
3050 return success;
3053 #if (ES_BUILD_PCL)
3054 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3056 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3058 #endif
3060 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3061 // When that is the case, we have the build the custom assemblies on a member by hand.
3062 internal static Attribute? GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3064 #if !ES_BUILD_PN
3065 // On ProjectN, ReflectionOnly() always equals false. AllowEventSourceOverride is an option that allows either Microsoft.Diagnostics.Tracing or
3066 // System.Diagnostics.Tracing EventSource to be considered valid. This should not mattter anywhere but in Microsoft.Diagnostics.Tracing (nuget package).
3067 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3068 #endif // !ES_BUILD_PN
3070 // Let the runtime to the work for us, since we can execute code in this context.
3071 Attribute? firstAttribute = null;
3072 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3074 firstAttribute = (Attribute)attribute;
3075 break;
3077 return firstAttribute;
3080 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3081 // In the reflection only context, we have to do things by hand.
3082 string fullTypeNameToFind = attributeType.FullName!;
3084 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3085 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3086 #endif
3088 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3090 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType!))
3092 Attribute? attr = null;
3094 Debug.Assert(data.ConstructorArguments.Count <= 1);
3096 if (data.ConstructorArguments.Count == 1)
3098 attr = (Attribute?)Activator.CreateInstance(attributeType, new object?[] { data.ConstructorArguments[0].Value });
3100 else if (data.ConstructorArguments.Count == 0)
3102 attr = (Attribute?)Activator.CreateInstance(attributeType);
3105 if (attr != null)
3107 Type t = attr.GetType();
3109 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3111 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance)!;
3112 object value = namedArgument.TypedValue.Value!;
3114 if (p.PropertyType.IsEnum)
3116 string val = value.ToString()!;
3117 value = Enum.Parse(p.PropertyType, val);
3120 p.SetValue(attr, value, null);
3123 return attr;
3128 return null;
3129 #else // ES_BUILD_PCL && ES_BUILD_PN
3130 // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
3131 throw new ArgumentException("EventSource_PCLPlatformNotSupportedReflection", "EventSource");
3132 #endif
3135 /// <summary>
3136 /// Evaluates if two related "EventSource"-domain types should be considered the same
3137 /// </summary>
3138 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3139 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3140 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3141 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3142 /// </param>
3143 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3144 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3146 return
3147 // are these the same type?
3148 attributeType == reflectedAttributeType ||
3149 // are the full typenames equal?
3150 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3151 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3152 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3153 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3154 attributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
3155 (reflectedAttributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
3156 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3157 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
3158 #endif
3162 private static Type? GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3164 Type? ret = eventSourceType;
3166 // return false for "object" and interfaces
3167 if (ret.BaseType() == null)
3168 return null;
3170 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3173 ret = ret.BaseType();
3175 while (ret != null && ret.IsAbstract());
3177 if (ret != null)
3179 if (!allowEventSourceOverride)
3181 if (reflectionOnly && ret.FullName != typeof(EventSource).FullName ||
3182 !reflectionOnly && ret != typeof(EventSource))
3183 return null;
3185 else
3187 if (ret.Name != "EventSource")
3188 return null;
3191 return ret;
3194 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3195 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3196 // at run time. 'source' is the event source to place the descriptors. If it is null,
3197 // then the descriptors are not creaed, and just the manifest is generated.
3198 private static byte[]? CreateManifestAndDescriptors(Type eventSourceType, string? eventSourceDllName, EventSource? source,
3199 EventManifestOptions flags = EventManifestOptions.None)
3201 ManifestBuilder? manifest = null;
3202 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3203 Exception? exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3204 byte[]? res = null;
3206 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3207 return null;
3211 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3212 EventAttribute defaultEventAttribute;
3213 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3214 EventMetadata[]? eventData = null;
3215 Dictionary<string, string>? eventsByName = null;
3216 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3218 eventData = new EventMetadata[methods.Length + 1];
3219 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3222 // See if we have localization information.
3223 ResourceManager? resources = null;
3224 EventSourceAttribute? eventSourceAttrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3225 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3226 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3228 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3229 resources, flags);
3231 // Add an entry unconditionally for event ID 0 which will be for a string message.
3232 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3233 manifest.AddEventParameter(typeof(string), "message");
3234 manifest.EndEvent();
3236 // eventSourceType must be sealed and must derive from this EventSource
3237 if ((flags & EventManifestOptions.Strict) != 0)
3239 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3241 if (!typeMatch)
3243 manifest.ManifestError(SR.EventSource_TypeMustDeriveFromEventSource);
3245 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3247 manifest.ManifestError(SR.EventSource_TypeMustBeSealedOrAbstract);
3251 // Collect task, opcode, keyword and channel information
3252 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3253 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3254 #else
3255 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3256 #endif
3258 Type? nestedType = eventSourceType.GetNestedType(providerEnumKind);
3259 if (nestedType != null)
3261 if (eventSourceType.IsAbstract())
3263 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareKTOC, nestedType.Name));
3265 else
3267 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3269 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3274 // ensure we have keywords for the session-filtering reserved bits
3276 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3277 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3278 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3279 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3282 if (eventSourceType != typeof(EventSource))
3284 for (int i = 0; i < methods.Length; i++)
3286 MethodInfo method = methods[i];
3287 ParameterInfo[] args = method.GetParameters();
3289 // Get the EventDescriptor (from the Custom attributes)
3290 EventAttribute? eventAttribute = (EventAttribute?)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3292 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3293 // the only reason of limiting the number of methods considered to be events. This broke a common
3294 // design of having event sources implement specific interfaces. To fix this in a compatible way
3295 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3296 // as they are marked with the [Event] attribute
3297 if (/* method.IsVirtual || */ method.IsStatic)
3299 continue;
3302 if (eventSourceType.IsAbstract())
3304 if (eventAttribute != null)
3306 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareEventMethods, method.Name, eventAttribute.EventId));
3308 continue;
3310 else if (eventAttribute == null)
3312 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3313 // (see Compat comment above)
3314 if (method.ReturnType != typeof(void))
3316 continue;
3319 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3320 // (see Compat comment above)
3321 if (method.IsVirtual)
3323 continue;
3326 // If we explicitly mark the method as not being an event, then honor that.
3327 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3328 continue;
3330 defaultEventAttribute = new EventAttribute(eventId);
3331 eventAttribute = defaultEventAttribute;
3333 else if (eventAttribute.EventId <= 0)
3335 manifest.ManifestError(SR.Format(SR.EventSource_NeedPositiveId, method.Name), true);
3336 continue; // don't validate anything else for this event
3338 if (method.Name.LastIndexOf('.') >= 0)
3340 manifest.ManifestError(SR.Format(SR.EventSource_EventMustNotBeExplicitImplementation, method.Name, eventAttribute.EventId));
3343 eventId++;
3344 string eventName = method.Name;
3346 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3348 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3349 bool noTask = (eventAttribute.Task == EventTask.None);
3350 if (noTask)
3351 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3353 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3354 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3355 if (!eventAttribute.IsOpcodeSet)
3356 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3358 // Make the stop opcode have the same task as the start opcode.
3359 if (noTask)
3361 if (eventAttribute.Opcode == EventOpcode.Start)
3363 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3364 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3365 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3367 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3368 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3369 manifest.AddTask(taskName, (int)eventAttribute.Task);
3372 else if (eventAttribute.Opcode == EventOpcode.Stop)
3374 // Find the start associated with this stop event. We require start to be immediately before the stop
3375 int startEventId = eventAttribute.EventId - 1;
3376 if (eventData != null && startEventId < eventData.Length)
3378 Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3379 EventMetadata startEventMetadata = eventData[startEventId];
3381 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3382 // Ideally we would throw an error
3383 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3384 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3385 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3386 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3389 // Make the stop event match the start event
3390 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3391 noTask = false;
3394 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3396 throw new ArgumentException(SR.EventSource_StopsFollowStarts);
3402 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3403 if (!(source != null && source.SelfDescribingEvents))
3405 manifest.StartEvent(eventName, eventAttribute);
3406 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3408 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name!);
3410 manifest.EndEvent();
3413 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3415 Debug.Assert(eventData != null);
3416 // Do checking for user errors (optional, but not a big deal so we do it).
3417 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3419 #if FEATURE_MANAGED_ETW_CHANNELS
3420 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3421 // and is not required for the manifest
3422 if (eventAttribute.Channel != EventChannel.None)
3424 unchecked
3426 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3429 #endif
3430 string eventKey = "event_" + eventName;
3431 string? msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3432 // overwrite inline message with the localized message
3433 if (msg != null) eventAttribute.Message = msg;
3435 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3440 // Tell the TraceLogging stuff where to start allocating its own IDs.
3441 NameInfo.ReserveEventIDsBelow(eventId);
3443 if (source != null)
3445 Debug.Assert(eventData != null);
3446 TrimEventDescriptors(ref eventData);
3447 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3448 #if FEATURE_MANAGED_ETW_CHANNELS
3449 source.m_channelData = manifest.GetChannelData();
3450 #endif
3453 // if this is an abstract event source we've already performed all the validation we can
3454 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3456 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3457 #if FEATURE_MANAGED_ETW_CHANNELS
3458 || manifest.GetChannelData().Length > 0
3459 #endif
3462 // if the manifest is not needed and we're not requested to validate the event source return early
3463 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3464 return null;
3466 res = manifest.CreateManifest();
3469 catch (Exception e)
3471 // if this is a runtime manifest generation let the exception propagate
3472 if ((flags & EventManifestOptions.Strict) == 0)
3473 throw;
3474 // else store it to include it in the Argument exception we raise below
3475 exception = e;
3478 if ((flags & EventManifestOptions.Strict) != 0 && (manifest?.Errors.Count > 0 || exception != null))
3480 string msg = string.Empty;
3482 if (manifest?.Errors.Count > 0)
3484 bool firstError = true;
3485 foreach (string error in manifest.Errors)
3487 if (!firstError)
3488 msg += Environment.NewLine;
3489 firstError = false;
3490 msg += error;
3493 else
3494 msg = "Unexpected error: " + exception!.Message;
3496 throw new ArgumentException(msg, exception);
3499 return bNeedsManifest ? res : null;
3502 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3504 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3505 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3506 string.Equals(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase))
3508 var newargs = new ParameterInfo[args.Length - 1];
3509 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3510 args = newargs;
3512 return true;
3515 return false;
3518 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3519 // to the manifest.
3520 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3522 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3523 Type staticFieldType = staticField.FieldType;
3524 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3526 if (providerEnumKind != "Opcodes") goto Error;
3527 int value = (int)staticField.GetRawConstantValue()!;
3528 manifest.AddOpcode(staticField.Name, value);
3530 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3532 if (providerEnumKind != "Tasks") goto Error;
3533 int value = (int)staticField.GetRawConstantValue()!;
3534 manifest.AddTask(staticField.Name, value);
3536 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3538 if (providerEnumKind != "Keywords") goto Error;
3539 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue()!);
3540 manifest.AddKeyword(staticField.Name, value);
3542 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3543 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3545 if (providerEnumKind != "Channels") goto Error;
3546 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3547 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3549 #endif
3550 return;
3551 Error:
3552 manifest.ManifestError(SR.Format(SR.EventSource_EnumKindMismatch, staticField.Name, staticField.FieldType.Name, providerEnumKind));
3555 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3556 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3557 // it is populated if we need to look up message resources
3558 private static void AddEventDescriptor(
3559 [NotNull] ref EventMetadata[] eventData,
3560 string eventName,
3561 EventAttribute eventAttribute,
3562 ParameterInfo[] eventParameters,
3563 bool hasRelatedActivityID)
3565 if (eventData.Length <= eventAttribute.EventId)
3567 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3568 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3569 eventData = newValues;
3572 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3573 eventAttribute.EventId,
3574 eventAttribute.Version,
3575 #if FEATURE_MANAGED_ETW_CHANNELS
3576 (byte)eventAttribute.Channel,
3577 #else
3578 (byte)0,
3579 #endif
3580 (byte)eventAttribute.Level,
3581 (byte)eventAttribute.Opcode,
3582 (int)eventAttribute.Task,
3583 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3585 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3586 eventData[eventAttribute.EventId].Name = eventName;
3587 eventData[eventAttribute.EventId].Parameters = eventParameters;
3588 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3589 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3590 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3591 eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero;
3594 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3595 // size after all event descriptors have been added.
3596 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3598 int idx = eventData.Length;
3599 while (0 < idx)
3601 --idx;
3602 if (eventData[idx].Descriptor.EventId != 0)
3603 break;
3605 if (eventData.Length - idx > 2) // allow one wasted slot.
3607 EventMetadata[] newValues = new EventMetadata[idx + 1];
3608 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3609 eventData = newValues;
3613 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3614 // when a listener gets attached to a eventSource
3615 internal void AddListener(EventListener listener)
3617 lock (EventListener.EventListenersLock)
3619 bool[]? enabledArray = null;
3620 if (m_eventData != null)
3621 enabledArray = new bool[m_eventData.Length];
3622 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3623 listener.OnEventSourceCreated(this);
3627 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3628 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3629 private static void DebugCheckEvent(ref Dictionary<string, string>? eventsByName,
3630 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3631 ManifestBuilder manifest, EventManifestOptions options)
3633 int evtId = eventAttribute.EventId;
3634 string evtName = method.Name;
3635 int eventArg = GetHelperCallFirstArg(method);
3636 if (eventArg >= 0 && evtId != eventArg)
3638 manifest.ManifestError(SR.Format(SR.EventSource_MismatchIdToWriteEvent, evtName, evtId, eventArg), true);
3641 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3643 manifest.ManifestError(SR.Format(SR.EventSource_EventIdReused, evtName, evtId, eventData[evtId].Name), true);
3646 // We give a task to things if they don't have one.
3647 // TODO this is moderately expensive (N*N). We probably should not even bother....
3648 Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3649 for (int idx = 0; idx < eventData.Length; ++idx)
3651 // skip unused Event IDs.
3652 if (eventData[idx].Name == null)
3653 continue;
3655 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3657 manifest.ManifestError(SR.Format(SR.EventSource_TaskOpcodePairReused,
3658 evtName, evtId, eventData[idx].Name, idx));
3659 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3660 if ((options & EventManifestOptions.Strict) == 0)
3661 break;
3665 // for non-default event opcodes the user must define a task!
3666 if (eventAttribute.Opcode != EventOpcode.Info)
3668 bool failure = false;
3669 if (eventAttribute.Task == EventTask.None)
3670 failure = true;
3671 else
3673 // If you have the auto-assigned Task, then you did not explicitly set one.
3674 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3675 // But all other cases we want to catch the omission.
3676 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3677 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3678 failure = true;
3680 if (failure)
3682 manifest.ManifestError(SR.Format(SR.EventSource_EventMustHaveTaskIfNonDefaultOpcode, evtName, evtId));
3686 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3687 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3688 // by eventRegister.exe)
3689 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3690 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3691 // {
3692 // throw new WarningException(SR.EventSource_EventNameDoesNotEqualTaskPlusOpcode);
3693 // }
3695 if (eventsByName == null)
3696 eventsByName = new Dictionary<string, string>();
3698 if (eventsByName.ContainsKey(evtName))
3700 manifest.ManifestError(SR.Format(SR.EventSource_EventNameReused, evtName), true);
3703 eventsByName[evtName] = evtName;
3706 /// <summary>
3707 /// This method looks at the IL and tries to pattern match against the standard
3708 /// 'boilerplate' event body
3709 /// <code>
3710 /// { if (Enabled()) WriteEvent(#, ...) }
3711 /// </code>
3712 /// If the pattern matches, it returns the literal number passed as the first parameter to
3713 /// the WriteEvent. This is used to find common user errors (mismatching this
3714 /// number with the EventAttribute ID). It is only used for validation.
3715 /// </summary>
3716 /// <param name="method">The method to probe.</param>
3717 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3718 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3719 private static int GetHelperCallFirstArg(MethodInfo method)
3721 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3722 // Currently searches for the following pattern
3724 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3725 // LDARG0
3726 // LDC.I4 XXX
3727 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3728 // CALL
3729 // NOP // 0 or more times
3730 // RET
3732 // If we find this pattern we return the XXX. Otherwise we return -1.
3733 #if ES_BUILD_STANDALONE
3734 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3735 #endif
3736 byte[] instrs = method.GetMethodBody()!.GetILAsByteArray()!;
3737 int retVal = -1;
3738 for (int idx = 0; idx < instrs.Length;)
3740 switch (instrs[idx])
3742 case 0: // NOP
3743 case 1: // BREAK
3744 case 2: // LDARG_0
3745 case 3: // LDARG_1
3746 case 4: // LDARG_2
3747 case 5: // LDARG_3
3748 case 6: // LDLOC_0
3749 case 7: // LDLOC_1
3750 case 8: // LDLOC_2
3751 case 9: // LDLOC_3
3752 case 10: // STLOC_0
3753 case 11: // STLOC_1
3754 case 12: // STLOC_2
3755 case 13: // STLOC_3
3756 break;
3757 case 14: // LDARG_S
3758 case 16: // STARG_S
3759 idx++;
3760 break;
3761 case 20: // LDNULL
3762 break;
3763 case 21: // LDC_I4_M1
3764 case 22: // LDC_I4_0
3765 case 23: // LDC_I4_1
3766 case 24: // LDC_I4_2
3767 case 25: // LDC_I4_3
3768 case 26: // LDC_I4_4
3769 case 27: // LDC_I4_5
3770 case 28: // LDC_I4_6
3771 case 29: // LDC_I4_7
3772 case 30: // LDC_I4_8
3773 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3774 retVal = instrs[idx] - 22;
3775 break;
3776 case 31: // LDC_I4_S
3777 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3778 retVal = instrs[idx + 1];
3779 idx++;
3780 break;
3781 case 32: // LDC_I4
3782 idx += 4;
3783 break;
3784 case 37: // DUP
3785 break;
3786 case 40: // CALL
3787 idx += 4;
3789 if (retVal >= 0)
3791 // Is this call just before return?
3792 for (int search = idx + 1; search < instrs.Length; search++)
3794 if (instrs[search] == 42) // RET
3795 return retVal;
3796 if (instrs[search] != 0) // NOP
3797 break;
3800 retVal = -1;
3801 break;
3802 case 44: // BRFALSE_S
3803 case 45: // BRTRUE_S
3804 retVal = -1;
3805 idx++;
3806 break;
3807 case 57: // BRFALSE
3808 case 58: // BRTRUE
3809 retVal = -1;
3810 idx += 4;
3811 break;
3812 case 103: // CONV_I1
3813 case 104: // CONV_I2
3814 case 105: // CONV_I4
3815 case 106: // CONV_I8
3816 case 109: // CONV_U4
3817 case 110: // CONV_U8
3818 break;
3819 case 140: // BOX
3820 case 141: // NEWARR
3821 idx += 4;
3822 break;
3823 case 162: // STELEM_REF
3824 break;
3825 case 254: // PREFIX
3826 idx++;
3827 // Covers the CEQ instructions used in debug code for some reason.
3828 if (idx >= instrs.Length || instrs[idx] >= 6)
3829 goto default;
3830 break;
3831 default:
3832 /* Debug.Fail("Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3833 " at " + idx + " in method " + method.Name); */
3834 return -1;
3836 idx++;
3838 #endif
3839 return -1;
3842 /// <summary>
3843 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3844 /// It will do this even if the EventSource is not enabled.
3845 /// TODO remove flush parameter it is not used.
3846 /// </summary>
3847 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3848 internal void ReportOutOfBandMessage(string msg, bool flush)
3852 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3853 // send message to debugger without delay
3854 System.Diagnostics.Debugger.Log(0, null, string.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
3855 #endif
3857 // Send it to all listeners.
3858 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3859 m_outOfBandMessageCount++;
3860 else
3862 if (m_outOfBandMessageCount == 16)
3863 return;
3864 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3865 msg = "Reached message limit. End of EventSource error messages.";
3868 WriteEventString(EventLevel.LogAlways, -1, msg);
3869 WriteStringToAllListeners("EventSourceMessage", msg);
3871 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3874 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3876 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3877 EventSourceSettings.EtwSelfDescribingEventFormat;
3878 if ((settings & evtFormatMask) == evtFormatMask)
3880 throw new ArgumentException(SR.EventSource_InvalidEventFormat, nameof(settings));
3883 // If you did not explicitly ask for manifest, you get self-describing.
3884 if ((settings & evtFormatMask) == 0)
3885 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3886 return settings;
3889 private bool ThrowOnEventWriteErrors
3891 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
3894 private bool SelfDescribingEvents
3898 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
3899 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
3900 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
3904 // private instance state
3905 private string m_name = null!; // My friendly name (privided in ctor)
3906 internal int m_id; // A small integer that is unique to this instance.
3907 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
3908 internal volatile EventMetadata[]? m_eventData; // None per-event data
3909 private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema
3911 private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;
3913 private EventSourceSettings m_config; // configuration information
3915 private bool m_eventSourceDisposed; // has Dispose been called.
3917 // Enabling bits
3918 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
3919 internal EventLevel m_level; // highest level enabled by any output dispatcher
3920 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
3922 // Dispatching state
3923 internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
3924 #if FEATURE_MANAGED_ETW
3925 private volatile OverideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
3926 #endif
3927 #if FEATURE_PERFTRACING
3928 private volatile OverideEventProvider m_eventPipeProvider = null!;
3929 #endif
3930 private bool m_completelyInited; // The EventSource constructor has returned without exception.
3931 private Exception? m_constructionException; // If there was an exception construction, this is it
3932 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
3933 private EventCommandEventArgs? m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
3935 private string[]? m_traits; // Used to implement GetTraits
3937 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
3938 [ThreadStatic]
3939 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
3941 [ThreadStatic]
3942 private static bool m_EventSourceInDecodeObject = false;
3944 #if FEATURE_MANAGED_ETW_CHANNELS
3945 internal volatile ulong[]? m_channelData;
3946 #endif
3948 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
3949 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
3950 ActivityTracker m_activityTracker = null!;
3951 internal const string s_ActivityStartSuffix = "Start";
3952 internal const string s_ActivityStopSuffix = "Stop";
3954 // WARNING: Do not depend upon initialized statics during creation of EventSources, as it is possible for creation of an EventSource to trigger
3955 // creation of yet another EventSource. When this happens, these statics may not yet be initialized.
3956 // Rather than depending on initialized statics, use lazy initialization to ensure that the statics are initialized exactly when they are needed.
3958 // used for generating GUID from eventsource name
3959 private static byte[] namespaceBytes;
3961 #endregion
3964 /// <summary>
3965 /// Enables specifying event source configuration options to be used in the EventSource constructor.
3966 /// </summary>
3967 [Flags]
3968 public enum EventSourceSettings
3970 /// <summary>
3971 /// This specifies none of the special configuration options should be enabled.
3972 /// </summary>
3973 Default = 0,
3974 /// <summary>
3975 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
3976 /// </summary>
3977 ThrowOnEventWriteErrors = 1,
3978 /// <summary>
3979 /// Setting this option is a directive to the ETW listener should use manifest-based format when
3980 /// firing events. This is the default option when defining a type derived from EventSource
3981 /// (using the protected EventSource constructors).
3982 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3983 /// </summary>
3984 EtwManifestEventFormat = 4,
3985 /// <summary>
3986 /// Setting this option is a directive to the ETW listener should use self-describing event format
3987 /// when firing events. This is the default option when creating a new instance of the EventSource
3988 /// type (using the public EventSource constructors).
3989 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3990 /// </summary>
3991 EtwSelfDescribingEventFormat = 8,
3994 /// <summary>
3995 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
3996 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
3997 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
3998 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
3999 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
4000 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
4001 /// longer needed.
4002 /// <para>
4003 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
4004 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
4005 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
4006 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
4007 /// </para><para>
4008 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
4009 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
4010 /// </para><para>
4011 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4012 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4013 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4014 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4015 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4016 /// </para><para>
4017 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4018 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4019 /// that eventSource what events that dispatcher will receive.
4020 /// </para><para>
4021 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4022 /// override this method to do something useful with the data.
4023 /// </para><para>
4024 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4025 /// invariant associated with this callback is that every eventSource gets exactly one
4026 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4027 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4028 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4029 /// created.
4030 /// </para>
4031 /// </summary>
4032 public class EventListener : IDisposable
4034 private event EventHandler<EventSourceCreatedEventArgs>? _EventSourceCreated;
4036 /// <summary>
4037 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4038 /// This can happen for all existing EventSources when the EventListener is created
4039 /// as well as for any EventSources that come into existence after the EventListener
4040 /// has been created.
4041 ///
4042 /// These 'catch up' events are called during the construction of the EventListener.
4043 /// Subclasses need to be prepared for that.
4044 ///
4045 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4046 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4047 /// </summary>
4048 public event EventHandler<EventSourceCreatedEventArgs>? EventSourceCreated
4052 CallBackForExistingEventSources(false, value);
4054 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Combine(_EventSourceCreated, value);
4056 remove
4058 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Remove(_EventSourceCreated, value);
4062 /// <summary>
4063 /// This event is raised whenever an event has been written by a EventSource for which
4064 /// the EventListener has enabled events.
4065 /// </summary>
4066 public event EventHandler<EventWrittenEventArgs>? EventWritten;
4068 static EventListener()
4070 #if FEATURE_PERFTRACING
4071 // Ensure that NativeRuntimeEventSource is initialized so that EventListeners get an opportunity to subscribe to its events.
4072 // This is required because NativeRuntimeEventSource never emit events on its own, and thus will never be initialized
4073 // in the normal way that EventSources are initialized.
4074 GC.KeepAlive(NativeRuntimeEventSource.Log);
4075 #endif // FEATURE_PERFTRACING
4078 /// <summary>
4079 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4080 /// them on).
4081 /// </summary>
4082 public EventListener()
4084 // This will cause the OnEventSourceCreated callback to fire.
4085 CallBackForExistingEventSources(true, (obj, args) =>
4087 args.EventSource!.AddListener((EventListener)obj!);
4091 /// <summary>
4092 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4093 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4094 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4095 /// call Dispose when they are done with their logging.
4096 /// </summary>
4097 #if ES_BUILD_STANDALONE
4098 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4099 #endif
4100 public virtual void Dispose()
4102 lock (EventListenersLock)
4104 if (s_Listeners != null)
4106 if (this == s_Listeners)
4108 EventListener cur = s_Listeners;
4109 s_Listeners = this.m_Next;
4110 RemoveReferencesToListenerInEventSources(cur);
4112 else
4114 // Find 'this' from the s_Listeners linked list.
4115 EventListener prev = s_Listeners;
4116 for (;;)
4118 EventListener? cur = prev.m_Next;
4119 if (cur == null)
4120 break;
4121 if (cur == this)
4123 // Found our Listener, remove references to it in the eventSources
4124 prev.m_Next = cur.m_Next; // Remove entry.
4125 RemoveReferencesToListenerInEventSources(cur);
4126 break;
4128 prev = cur;
4132 Validate();
4135 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4136 // 'cleanup' associated with this object
4138 /// <summary>
4139 /// Enable all events from the eventSource identified by 'eventSource' to the current
4140 /// dispatcher that have a verbosity level of 'level' or lower.
4141 ///
4142 /// This call can have the effect of REDUCING the number of events sent to the
4143 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4144 ///
4145 /// This call never has an effect on other EventListeners.
4147 /// </summary>
4148 public void EnableEvents(EventSource eventSource, EventLevel level)
4150 EnableEvents(eventSource, level, EventKeywords.None);
4152 /// <summary>
4153 /// Enable all events from the eventSource identified by 'eventSource' to the current
4154 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4155 /// matching any of the bits in 'matchAnyKeyword'.
4156 ///
4157 /// This call can have the effect of REDUCING the number of events sent to the
4158 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4159 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4160 ///
4161 /// This call never has an effect on other EventListeners.
4162 /// </summary>
4163 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4165 EnableEvents(eventSource, level, matchAnyKeyword, null);
4167 /// <summary>
4168 /// Enable all events from the eventSource identified by 'eventSource' to the current
4169 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4170 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4171 /// effect passing additional 'key-value' arguments 'arguments' might have.
4172 ///
4173 /// This call can have the effect of REDUCING the number of events sent to the
4174 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4175 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4176 ///
4177 /// This call never has an effect on other EventListeners.
4178 /// </summary>
4179 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string?>? arguments)
4181 if (eventSource == null)
4183 throw new ArgumentNullException(nameof(eventSource));
4186 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4188 #if FEATURE_PERFTRACING
4189 if (eventSource.GetType() == typeof(NativeRuntimeEventSource))
4191 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, true, level, matchAnyKeyword);
4193 #endif // FEATURE_PERFTRACING
4195 /// <summary>
4196 /// Disables all events coming from eventSource identified by 'eventSource'.
4197 ///
4198 /// This call never has an effect on other EventListeners.
4199 /// </summary>
4200 public void DisableEvents(EventSource eventSource)
4202 if (eventSource == null)
4204 throw new ArgumentNullException(nameof(eventSource));
4207 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4209 #if FEATURE_PERFTRACING
4210 if (eventSource.GetType() == typeof(NativeRuntimeEventSource))
4212 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None);
4214 #endif // FEATURE_PERFTRACING
4217 /// <summary>
4218 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4219 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4220 /// it useful to store additional information about each eventSource connected to it,
4221 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4222 /// (growable) array (eg List(T)).
4223 /// </summary>
4224 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4226 /// <summary>
4227 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4228 /// This can happen for all existing EventSources when the EventListener is created
4229 /// as well as for any EventSources that come into existence after the EventListener
4230 /// has been created.
4231 ///
4232 /// These 'catch up' events are called during the construction of the EventListener.
4233 /// Subclasses need to be prepared for that.
4234 ///
4235 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4236 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4237 /// </summary>
4238 /// <param name="eventSource"></param>
4239 internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4241 EventHandler<EventSourceCreatedEventArgs>? callBack = this._EventSourceCreated;
4242 if (callBack != null)
4244 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4245 args.EventSource = eventSource;
4246 callBack(this, args);
4250 /// <summary>
4251 /// This method is called whenever an event has been written by a EventSource for which
4252 /// the EventListener has enabled events.
4253 /// </summary>
4254 /// <param name="eventData"></param>
4255 internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4257 EventHandler<EventWrittenEventArgs>? callBack = this.EventWritten;
4258 if (callBack != null)
4260 callBack(this, eventData);
4265 #region private
4266 /// <summary>
4267 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4268 /// ID to the eventSource (which is simply the ordinal in the global list).
4269 ///
4270 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4271 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4272 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4273 /// that are in the list). This seems OK since the expectation is that EventSources
4274 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4275 /// global variables).
4276 /// </summary>
4277 /// <param name="newEventSource"></param>
4278 internal static void AddEventSource(EventSource newEventSource)
4280 lock (EventListenersLock)
4282 if (s_EventSources == null)
4283 s_EventSources = new List<WeakReference>(2);
4285 if (!s_EventSourceShutdownRegistered)
4287 s_EventSourceShutdownRegistered = true;
4288 #if ES_BUILD_STANDALONE
4289 AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
4290 AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
4291 #else
4292 AppContext.ProcessExit += DisposeOnShutdown;
4293 #endif
4297 // Periodically search the list for existing entries to reuse, this avoids
4298 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4299 int newIndex = -1;
4300 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4302 int i = s_EventSources.Count; // Work from the top down.
4303 while (0 < i)
4305 --i;
4306 WeakReference weakRef = s_EventSources[i];
4307 if (!weakRef.IsAlive)
4309 newIndex = i;
4310 weakRef.Target = newEventSource;
4311 break;
4315 if (newIndex < 0)
4317 newIndex = s_EventSources.Count;
4318 s_EventSources.Add(new WeakReference(newEventSource));
4320 newEventSource.m_id = newIndex;
4322 #if DEBUG
4323 // Disable validation of EventSource/EventListener connections in case a call to EventSource.AddListener
4324 // causes a recursive call into this method.
4325 bool previousValue = s_ConnectingEventSourcesAndListener;
4326 s_ConnectingEventSourcesAndListener = true;
4329 #endif
4330 // Add every existing dispatcher to the new EventSource
4331 for (EventListener? listener = s_Listeners; listener != null; listener = listener.m_Next)
4332 newEventSource.AddListener(listener);
4333 #if DEBUG
4335 finally
4337 s_ConnectingEventSourcesAndListener = previousValue;
4339 #endif
4341 Validate();
4345 // Whenver we have async callbacks from native code, there is an ugly issue where
4346 // during .NET shutdown native code could be calling the callback, but the CLR
4347 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4348 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4349 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4350 // do this. This is what this callback is for.
4351 // See bug 724140 for more
4352 private static void DisposeOnShutdown(object? sender, EventArgs e)
4354 lock (EventListenersLock)
4356 Debug.Assert(s_EventSources != null);
4357 foreach (var esRef in s_EventSources)
4359 if (esRef.Target is EventSource es)
4360 es.Dispose();
4365 /// <summary>
4366 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4367 /// eventSources in the appdomain.
4368 ///
4369 /// The EventListenersLock must be held before calling this routine.
4370 /// </summary>
4371 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4373 #if !ES_BUILD_STANDALONE
4374 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4375 #endif
4376 // Foreach existing EventSource in the appdomain
4377 Debug.Assert(s_EventSources != null);
4378 foreach (WeakReference eventSourceRef in s_EventSources)
4380 if (eventSourceRef.Target is EventSource eventSource)
4382 Debug.Assert(eventSource.m_Dispatchers != null);
4383 // Is the first output dispatcher the dispatcher we are removing?
4384 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4385 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4386 else
4388 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4389 EventDispatcher? prev = eventSource.m_Dispatchers;
4390 for (;;)
4392 EventDispatcher? cur = prev.m_Next;
4393 if (cur == null)
4395 Debug.Fail("EventSource did not have a registered EventListener!");
4396 break;
4398 if (cur.m_Listener == listenerToRemove)
4400 prev.m_Next = cur.m_Next; // Remove entry.
4401 break;
4403 prev = cur;
4409 #if FEATURE_PERFTRACING
4410 // Remove the listener from the EventPipe dispatcher.
4411 EventPipeEventDispatcher.Instance.RemoveEventListener(listenerToRemove);
4412 #endif // FEATURE_PERFTRACING
4415 /// <summary>
4416 /// Checks internal consistency of EventSources/Listeners.
4417 /// </summary>
4418 [Conditional("DEBUG")]
4419 internal static void Validate()
4421 #if DEBUG
4422 // Don't run validation code if we're in the middle of modifying the connections between EventSources and EventListeners.
4423 if (s_ConnectingEventSourcesAndListener)
4425 return;
4427 #endif
4429 lock (EventListenersLock)
4431 Debug.Assert(s_EventSources != null);
4432 // Get all listeners
4433 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4434 EventListener? cur = s_Listeners;
4435 while (cur != null)
4437 allListeners.Add(cur, true);
4438 cur = cur.m_Next;
4441 // For all eventSources
4442 int id = -1;
4443 foreach (WeakReference eventSourceRef in s_EventSources)
4445 id++;
4446 if (!(eventSourceRef.Target is EventSource eventSource))
4447 continue;
4448 Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4450 // None listeners on eventSources exist in the dispatcher list.
4451 EventDispatcher? dispatcher = eventSource.m_Dispatchers;
4452 while (dispatcher != null)
4454 Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4455 dispatcher = dispatcher.m_Next;
4458 // Every dispatcher is on Dispatcher List of every eventSource.
4459 foreach (EventListener listener in allListeners.Keys)
4461 dispatcher = eventSource.m_Dispatchers;
4462 for (;;)
4464 Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4465 if (dispatcher.m_Listener == listener)
4466 break;
4467 dispatcher = dispatcher.m_Next;
4474 /// <summary>
4475 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4476 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4477 /// the lock object)
4478 /// </summary>
4479 internal static object EventListenersLock
4483 if (s_EventSources == null)
4484 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4485 return s_EventSources;
4489 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs>? callback)
4491 lock (EventListenersLock)
4493 Debug.Assert(s_EventSources != null);
4495 // Disallow creating EventListener reentrancy.
4496 if (s_CreatingListener)
4498 throw new InvalidOperationException(SR.EventSource_ListenerCreatedInsideCallback);
4503 s_CreatingListener = true;
4505 if (addToListenersList)
4507 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4508 // Those added sources see this listener.
4509 this.m_Next = s_Listeners;
4510 s_Listeners = this;
4513 if (callback != null)
4515 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4516 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4517 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4518 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4519 // is created.
4520 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4522 #if DEBUG
4523 bool previousValue = s_ConnectingEventSourcesAndListener;
4524 s_ConnectingEventSourcesAndListener = true;
4527 #endif
4528 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4530 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4531 if (eventSourceRef.Target is EventSource eventSource)
4533 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4534 args.EventSource = eventSource;
4535 callback(this, args);
4538 #if DEBUG
4540 finally
4542 s_ConnectingEventSourcesAndListener = previousValue;
4544 #endif
4547 Validate();
4549 finally
4551 s_CreatingListener = false;
4557 // Instance fields
4558 internal volatile EventListener? m_Next; // These form a linked list in s_Listeners
4560 // static fields
4562 /// <summary>
4563 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4564 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4565 /// </summary>
4566 internal static EventListener? s_Listeners;
4567 /// <summary>
4568 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4569 /// remove themselves from this list this is a weak list and the GC that removes them may
4570 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4571 /// to filter those out.
4572 /// </summary>
4573 internal static List<WeakReference>? s_EventSources;
4575 /// <summary>
4576 /// Used to disallow reentrancy.
4577 /// </summary>
4578 private static bool s_CreatingListener = false;
4580 #if DEBUG
4581 /// <summary>
4582 /// Used to disable validation of EventSource and EventListener connectivity.
4583 /// This is needed when an EventListener is in the middle of being published to all EventSources
4584 /// and another EventSource is created as part of the process.
4585 /// </summary>
4586 [ThreadStatic]
4587 private static bool s_ConnectingEventSourcesAndListener = false;
4588 #endif
4590 /// <summary>
4591 /// Used to register AD/Process shutdown callbacks.
4592 /// </summary>
4593 private static bool s_EventSourceShutdownRegistered = false;
4594 #endregion
4597 /// <summary>
4598 /// Passed to the code:EventSource.OnEventCommand callback
4599 /// </summary>
4600 public class EventCommandEventArgs : EventArgs
4602 /// <summary>
4603 /// Gets the command for the callback.
4604 /// </summary>
4605 public EventCommand Command { get; internal set; }
4607 /// <summary>
4608 /// Gets the arguments for the callback.
4609 /// </summary>
4610 public IDictionary<string, string?>? Arguments { get; internal set; }
4612 /// <summary>
4613 /// Enables the event that has the specified identifier.
4614 /// </summary>
4615 /// <param name="eventId">Event ID of event to be enabled</param>
4616 /// <returns>true if eventId is in range</returns>
4617 public bool EnableEvent(int eventId)
4619 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4620 throw new InvalidOperationException();
4621 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, true);
4624 /// <summary>
4625 /// Disables the event that have the specified identifier.
4626 /// </summary>
4627 /// <param name="eventId">Event ID of event to be disabled</param>
4628 /// <returns>true if eventId is in range</returns>
4629 public bool DisableEvent(int eventId)
4631 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4632 throw new InvalidOperationException();
4633 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, false);
4636 #region private
4638 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string?>? arguments, EventSource eventSource,
4639 EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4641 this.Command = command;
4642 this.Arguments = arguments;
4643 this.eventSource = eventSource;
4644 this.listener = listener;
4645 this.eventProviderType = eventProviderType;
4646 this.perEventSourceSessionId = perEventSourceSessionId;
4647 this.etwSessionId = etwSessionId;
4648 this.enable = enable;
4649 this.level = level;
4650 this.matchAnyKeyword = matchAnyKeyword;
4653 internal EventSource eventSource;
4654 internal EventDispatcher? dispatcher;
4655 internal EventProviderType eventProviderType;
4657 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4658 internal EventListener? listener;
4659 internal int perEventSourceSessionId;
4660 internal int etwSessionId;
4661 internal bool enable;
4662 internal EventLevel level;
4663 internal EventKeywords matchAnyKeyword;
4664 internal EventCommandEventArgs? nextCommand; // We form a linked list of these deferred commands.
4666 #endregion
4669 /// <summary>
4670 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4671 /// </summary>
4672 public class EventSourceCreatedEventArgs : EventArgs
4674 /// <summary>
4675 /// The EventSource that is attaching to the listener.
4676 /// </summary>
4677 public EventSource? EventSource
4679 get;
4680 internal set;
4684 /// <summary>
4685 /// EventWrittenEventArgs is passed to the user-provided override for
4686 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4687 /// </summary>
4688 public class EventWrittenEventArgs : EventArgs
4690 /// <summary>
4691 /// The name of the event.
4692 /// </summary>
4693 public string? EventName
4697 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4699 return m_eventName;
4701 else
4703 Debug.Assert(m_eventSource.m_eventData != null);
4704 return m_eventSource.m_eventData[EventId].Name;
4707 internal set
4709 m_eventName = value;
4713 /// <summary>
4714 /// Gets the event ID for the event that was written.
4715 /// </summary>
4716 public int EventId { get; internal set; }
4718 /// <summary>
4719 /// Gets the activity ID for the thread on which the event was written.
4720 /// </summary>
4721 public Guid ActivityId
4725 Guid activityId = m_activityId;
4726 if (activityId == Guid.Empty)
4728 activityId = EventSource.CurrentThreadActivityId;
4731 return activityId;
4733 internal set
4735 m_activityId = value;
4739 /// <summary>
4740 /// Gets the related activity ID if one was specified when the event was written.
4741 /// </summary>
4742 public Guid RelatedActivityId
4744 get;
4745 internal set;
4748 /// <summary>
4749 /// Gets the payload for the event.
4750 /// </summary>
4751 public ReadOnlyCollection<object?>? Payload { get; internal set; }
4753 /// <summary>
4754 /// Gets the payload argument names.
4755 /// </summary>
4756 public ReadOnlyCollection<string>? PayloadNames
4760 // For contract based events we create the list lazily.
4761 // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only
4762 // do the lazy init if you know it is contract based (EventID >= 0)
4763 if (EventId >= 0 && m_payloadNames == null)
4766 var names = new List<string>();
4767 Debug.Assert(m_eventSource.m_eventData != null);
4768 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4770 names.Add(parameter.Name!);
4773 m_payloadNames = new ReadOnlyCollection<string>(names);
4776 return m_payloadNames;
4779 internal set
4781 m_payloadNames = value;
4785 /// <summary>
4786 /// Gets the event source object.
4787 /// </summary>
4788 public EventSource EventSource { get { return m_eventSource; } }
4790 /// <summary>
4791 /// Gets the keywords for the event.
4792 /// </summary>
4793 public EventKeywords Keywords
4797 if (EventId < 0) // TraceLogging convention EventID == -1
4798 return m_keywords;
4800 Debug.Assert(m_eventSource.m_eventData != null);
4801 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4805 /// <summary>
4806 /// Gets the operation code for the event.
4807 /// </summary>
4808 public EventOpcode Opcode
4812 if (EventId <= 0) // TraceLogging convention EventID == -1
4813 return m_opcode;
4815 Debug.Assert(m_eventSource.m_eventData != null);
4816 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4820 /// <summary>
4821 /// Gets the task for the event.
4822 /// </summary>
4823 public EventTask Task
4827 if (EventId <= 0) // TraceLogging convention EventID == -1
4828 return EventTask.None;
4830 Debug.Assert(m_eventSource.m_eventData != null);
4831 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4835 /// <summary>
4836 /// Any provider/user defined options associated with the event.
4837 /// </summary>
4838 public EventTags Tags
4842 if (EventId <= 0) // TraceLogging convention EventID == -1
4843 return m_tags;
4845 Debug.Assert(m_eventSource.m_eventData != null);
4846 return m_eventSource.m_eventData[EventId].Tags;
4850 /// <summary>
4851 /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
4852 /// </summary>
4853 public string? Message
4857 if (EventId <= 0) // TraceLogging convention EventID == -1
4859 return m_message;
4861 else
4863 Debug.Assert(m_eventSource.m_eventData != null);
4864 return m_eventSource.m_eventData[EventId].Message;
4867 internal set
4869 m_message = value;
4874 #if FEATURE_MANAGED_ETW_CHANNELS
4875 /// <summary>
4876 /// Gets the channel for the event.
4877 /// </summary>
4878 public EventChannel Channel
4882 if (EventId <= 0) // TraceLogging convention EventID == -1
4883 return EventChannel.None;
4885 Debug.Assert(m_eventSource.m_eventData != null);
4886 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4889 #endif
4891 /// <summary>
4892 /// Gets the version of the event.
4893 /// </summary>
4894 public byte Version
4898 if (EventId <= 0) // TraceLogging convention EventID == -1
4899 return 0;
4901 Debug.Assert(m_eventSource.m_eventData != null);
4902 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4906 /// <summary>
4907 /// Gets the level for the event.
4908 /// </summary>
4909 public EventLevel Level
4913 if (EventId <= 0) // TraceLogging convention EventID == -1
4914 return m_level;
4916 Debug.Assert(m_eventSource.m_eventData != null);
4917 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4921 /// <summary>
4922 /// Gets the identifier for the OS thread that wrote the event.
4923 /// </summary>
4924 public long OSThreadId
4928 if (!m_osThreadId.HasValue)
4930 #if ES_BUILD_STANDALONE
4931 m_osThreadId = (long)Interop.Kernel32.GetCurrentThreadId();
4932 #else
4933 m_osThreadId = (long)Thread.CurrentOSThreadId;
4934 #endif
4937 return m_osThreadId.Value;
4939 internal set
4941 m_osThreadId = value;
4945 /// <summary>
4946 /// Gets a UTC DateTime that specifies when the event was written.
4947 /// </summary>
4948 public DateTime TimeStamp
4950 get;
4951 internal set;
4954 #region private
4955 internal EventWrittenEventArgs(EventSource eventSource)
4957 m_eventSource = eventSource;
4958 TimeStamp = DateTime.UtcNow;
4960 private string? m_message;
4961 private string? m_eventName;
4962 private EventSource m_eventSource;
4963 private ReadOnlyCollection<string>? m_payloadNames;
4964 private Guid m_activityId;
4965 private long? m_osThreadId;
4966 internal EventTags m_tags;
4967 internal EventOpcode m_opcode;
4968 internal EventLevel m_level;
4969 internal EventKeywords m_keywords;
4970 #endregion
4973 /// <summary>
4974 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4975 /// </summary>
4976 [AttributeUsage(AttributeTargets.Class)]
4977 public sealed class EventSourceAttribute : Attribute
4979 /// <summary>
4980 /// Overrides the ETW name of the event source (which defaults to the class name)
4981 /// </summary>
4982 public string? Name { get; set; }
4984 /// <summary>
4985 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
4986 /// except when upgrading existing ETW providers to using event sources.
4987 /// </summary>
4988 public string? Guid { get; set; }
4990 /// <summary>
4991 /// <para>
4992 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
4993 /// can be localized to several languages if desired. This works by creating a ResX style string table
4994 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
4995 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
4996 /// resources. This name is the value of the LocalizationResources property.
4997 /// </para><para>
4998 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
4999 /// using the following resource naming scheme
5000 /// </para>
5001 /// <para>* event_EVENTNAME</para>
5002 /// <para>* task_TASKNAME</para>
5003 /// <para>* keyword_KEYWORDNAME</para>
5004 /// <para>* map_MAPNAME</para>
5005 /// <para>
5006 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
5007 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
5008 /// which represent the payload values.
5009 /// </para>
5010 /// </summary>
5011 public string? LocalizationResources { get; set; }
5014 /// <summary>
5015 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5016 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5017 /// name of the method and its signature to generate basic schema information for the event. The
5018 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5019 /// desired.
5020 /// </summary>
5021 [AttributeUsage(AttributeTargets.Method)]
5022 public sealed class EventAttribute : Attribute
5024 /// <summary>Construct an EventAttribute with specified eventId</summary>
5025 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
5026 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5027 /// <summary>Event's ID</summary>
5028 public int EventId { get; private set; }
5029 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5030 public EventLevel Level { get; set; }
5031 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5032 public EventKeywords Keywords { get; set; }
5033 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5034 public EventOpcode Opcode
5038 return m_opcode;
5042 this.m_opcode = value;
5043 this.m_opcodeSet = true;
5047 internal bool IsOpcodeSet
5051 return m_opcodeSet;
5055 /// <summary>Event's task: allows logical grouping of events</summary>
5056 public EventTask Task { get; set; }
5057 #if FEATURE_MANAGED_ETW_CHANNELS
5058 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5059 public EventChannel Channel { get; set; }
5060 #endif
5061 /// <summary>Event's version</summary>
5062 public byte Version { get; set; }
5064 /// <summary>
5065 /// This can be specified to enable formatting and localization of the event's payload. You can
5066 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5067 /// with the 'ToString()' of the corresponding part of the event payload.
5068 /// </summary>
5069 public string? Message { get; set; }
5071 /// <summary>
5072 /// User defined options associated with the event. These do not have meaning to the EventSource but
5073 /// are passed through to listeners which given them semantics.
5074 /// </summary>
5075 public EventTags Tags { get; set; }
5077 /// <summary>
5078 /// Allows fine control over the Activity IDs generated by start and stop events
5079 /// </summary>
5080 public EventActivityOptions ActivityOptions { get; set; }
5082 #region private
5083 EventOpcode m_opcode;
5084 private bool m_opcodeSet;
5085 #endregion
5088 /// <summary>
5089 /// By default all instance methods in a class that subclasses code:EventSource that and return
5090 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5091 /// the code:NonEventAttribute
5092 /// </summary>
5093 [AttributeUsage(AttributeTargets.Method)]
5094 public sealed class NonEventAttribute : Attribute
5096 /// <summary>
5097 /// Constructs a default NonEventAttribute
5098 /// </summary>
5099 public NonEventAttribute() { }
5102 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5103 #if FEATURE_MANAGED_ETW_CHANNELS
5104 /// <summary>
5105 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5106 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5107 /// <code>
5108 /// public static class Channels
5109 /// {
5110 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5111 /// public const EventChannel Admin = (EventChannel)16;
5112 ///
5113 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5114 /// public const EventChannel Operational = (EventChannel)17;
5115 /// }
5116 /// </code>
5117 /// </summary>
5118 [AttributeUsage(AttributeTargets.Field)]
5119 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5120 public
5121 #endif
5122 class EventChannelAttribute : Attribute
5124 /// <summary>
5125 /// Specified whether the channel is enabled by default
5126 /// </summary>
5127 public bool Enabled { get; set; }
5129 /// <summary>
5130 /// Legal values are in EventChannelType
5131 /// </summary>
5132 public EventChannelType EventChannelType { get; set; }
5134 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5135 /// <summary>
5136 /// Specifies the isolation for the channel
5137 /// </summary>
5138 public EventChannelIsolation Isolation { get; set; }
5140 /// <summary>
5141 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5142 /// See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype) for details.
5143 /// </summary>
5144 public string? Access { get; set; }
5146 /// <summary>
5147 /// Allows importing channels defined in external manifests
5148 /// </summary>
5149 public string? ImportChannel { get; set; }
5150 #endif
5152 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
5153 // public string Name { get; set; }
5156 /// <summary>
5157 /// Allowed channel types
5158 /// </summary>
5159 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5160 public
5161 #endif
5162 enum EventChannelType
5164 /// <summary>The admin channel</summary>
5165 Admin = 1,
5166 /// <summary>The operational channel</summary>
5167 Operational,
5168 /// <summary>The Analytic channel</summary>
5169 Analytic,
5170 /// <summary>The debug channel</summary>
5171 Debug,
5174 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5175 /// <summary>
5176 /// Allowed isolation levels. See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype)
5177 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5178 /// access permissions for the channel and backing file.
5179 /// </summary>
5180 public
5181 enum EventChannelIsolation
5183 /// <summary>
5184 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5185 /// </summary>
5186 Application = 1,
5187 /// <summary>
5188 /// All channels that specify System isolation use the same ETW session
5189 /// </summary>
5190 System,
5191 /// <summary>
5192 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5193 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5194 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5195 /// </summary>
5196 Custom,
5198 #endif
5199 #endif
5201 /// <summary>
5202 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5203 /// </summary>
5204 public enum EventCommand
5206 /// <summary>
5207 /// Update EventSource state
5208 /// </summary>
5209 Update = 0,
5210 /// <summary>
5211 /// Request EventSource to generate and send its manifest
5212 /// </summary>
5213 SendManifest = -1,
5214 /// <summary>
5215 /// Enable event
5216 /// </summary>
5217 Enable = -2,
5218 /// <summary>
5219 /// Disable event
5220 /// </summary>
5221 Disable = -3
5225 #region private classes
5227 // holds a bitfield representing a session mask
5228 /// <summary>
5229 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5230 /// is the index in the SessionMask of the bit that will be set. These can translate to
5231 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5232 /// FromEventKeywords() methods.
5233 /// </summary>
5234 internal struct SessionMask
5236 public SessionMask(SessionMask m)
5237 { m_mask = m.m_mask; }
5239 public SessionMask(uint mask = 0)
5240 { m_mask = mask & MASK; }
5242 public bool IsEqualOrSupersetOf(SessionMask m)
5244 return (this.m_mask | m.m_mask) == this.m_mask;
5247 public static SessionMask All
5249 get { return new SessionMask(MASK); }
5252 public static SessionMask FromId(int perEventSourceSessionId)
5254 Debug.Assert(perEventSourceSessionId < MAX);
5255 return new SessionMask((uint)1 << perEventSourceSessionId);
5258 public ulong ToEventKeywords()
5260 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5263 public static SessionMask FromEventKeywords(ulong m)
5265 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5268 public bool this[int perEventSourceSessionId]
5272 Debug.Assert(perEventSourceSessionId < MAX);
5273 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5277 Debug.Assert(perEventSourceSessionId < MAX);
5278 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5279 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5283 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5285 return new SessionMask(m1.m_mask | m2.m_mask);
5288 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5290 return new SessionMask(m1.m_mask & m2.m_mask);
5293 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5295 return new SessionMask(m1.m_mask ^ m2.m_mask);
5298 public static SessionMask operator ~(SessionMask m)
5300 return new SessionMask(MASK & ~(m.m_mask));
5303 public static explicit operator ulong(SessionMask m)
5304 { return m.m_mask; }
5306 public static explicit operator uint(SessionMask m)
5307 { return m.m_mask; }
5309 private uint m_mask;
5311 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5312 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5313 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5316 /// <summary>
5317 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5318 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5319 ///
5320 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5321 /// that EventListener has activate) and a Single EventSource may also have many
5322 /// event Dispatchers (one for every EventListener that has activated it).
5323 ///
5324 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5325 /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is
5326 /// associated with.
5327 /// </summary>
5328 internal class EventDispatcher
5330 internal EventDispatcher(EventDispatcher? next, bool[]? eventEnabled, EventListener listener)
5332 m_Next = next;
5333 m_EventEnabled = eventEnabled;
5334 m_Listener = listener;
5337 // Instance fields
5338 readonly internal EventListener m_Listener; // The dispatcher this entry is for
5339 internal bool[]? m_EventEnabled; // For every event in a the eventSource, is it enabled?
5341 // Only guaranteed to exist after a InsureInit()
5342 internal EventDispatcher? m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5343 // Of all listeners for that eventSource.
5346 /// <summary>
5347 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5348 /// generated.
5349 /// </summary>
5350 [Flags]
5351 public enum EventManifestOptions
5353 /// <summary>
5354 /// Only the resources associated with current UI culture are included in the manifest
5355 /// </summary>
5356 None = 0x0,
5357 /// <summary>
5358 /// Throw exceptions for any inconsistency encountered
5359 /// </summary>
5360 Strict = 0x1,
5361 /// <summary>
5362 /// Generate a "resources" node under "localization" for every satellite assembly provided
5363 /// </summary>
5364 AllCultures = 0x2,
5365 /// <summary>
5366 /// Generate the manifest only if the event source needs to be registered on the machine,
5367 /// otherwise return null (but still perform validation if Strict is specified)
5368 /// </summary>
5369 OnlyIfNeededForRegistration = 0x4,
5370 /// <summary>
5371 /// When generating the manifest do *not* enforce the rule that the current EventSource class
5372 /// must be the base class for the user-defined type passed in. This allows validation of .net
5373 /// event sources using the new validation code
5374 /// </summary>
5375 AllowEventSourceOverride = 0x8,
5378 /// <summary>
5379 /// ManifestBuilder is designed to isolate the details of the message of the event from the
5380 /// rest of EventSource. This one happens to create XML.
5381 /// </summary>
5382 internal class ManifestBuilder
5384 /// <summary>
5385 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
5386 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
5387 /// </summary>
5388 public ManifestBuilder(string providerName, Guid providerGuid, string? dllName, ResourceManager? resources,
5389 EventManifestOptions flags)
5391 #if FEATURE_MANAGED_ETW_CHANNELS
5392 this.providerName = providerName;
5393 #endif
5394 this.flags = flags;
5396 this.resources = resources;
5397 sb = new StringBuilder();
5398 events = new StringBuilder();
5399 templates = new StringBuilder();
5400 opcodeTab = new Dictionary<int, string>();
5401 stringTab = new Dictionary<string, string>();
5402 errors = new List<string>();
5403 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
5405 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5406 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\">");
5407 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5408 sb.Append("<provider name=\"").Append(providerName).
5409 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
5410 if (dllName != null)
5411 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
5413 var symbolsName = providerName.Replace("-", "").Replace('.', '_'); // Period and - are illegal replace them.
5414 sb.Append("\" symbol=\"").Append(symbolsName);
5415 sb.Append("\">").AppendLine();
5418 public void AddOpcode(string name, int value)
5420 if ((flags & EventManifestOptions.Strict) != 0)
5422 if (value <= 10 || value >= 239)
5424 ManifestError(SR.Format(SR.EventSource_IllegalOpcodeValue, name, value));
5427 if (opcodeTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5429 ManifestError(SR.Format(SR.EventSource_OpcodeCollision, name, prevName, value));
5432 opcodeTab[value] = name;
5434 public void AddTask(string name, int value)
5436 if ((flags & EventManifestOptions.Strict) != 0)
5438 if (value <= 0 || value >= 65535)
5440 ManifestError(SR.Format(SR.EventSource_IllegalTaskValue, name, value));
5443 if (taskTab != null && taskTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5445 ManifestError(SR.Format(SR.EventSource_TaskCollision, name, prevName, value));
5448 if (taskTab == null)
5449 taskTab = new Dictionary<int, string>();
5450 taskTab[value] = name;
5452 public void AddKeyword(string name, ulong value)
5454 if ((value & (value - 1)) != 0) // Is it a power of 2?
5456 ManifestError(SR.Format(SR.EventSource_KeywordNeedPowerOfTwo, "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
5458 if ((flags & EventManifestOptions.Strict) != 0)
5460 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
5462 ManifestError(SR.Format(SR.EventSource_IllegalKeywordsValue, name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5465 if (keywordTab != null && keywordTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5467 ManifestError(SR.Format(SR.EventSource_KeywordCollision, name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5470 if (keywordTab == null)
5471 keywordTab = new Dictionary<ulong, string>();
5472 keywordTab[value] = name;
5475 #if FEATURE_MANAGED_ETW_CHANNELS
5476 /// <summary>
5477 /// Add a channel. channelAttribute can be null
5478 /// </summary>
5479 public void AddChannel(string? name, int value, EventChannelAttribute? channelAttribute)
5481 EventChannel chValue = (EventChannel)value;
5482 if (value < (int)EventChannel.Admin || value > 255)
5483 ManifestError(SR.Format(SR.EventSource_EventChannelOutOfRange, name, value));
5484 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
5485 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
5487 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
5488 // but we want to allow them to override the default ones...
5489 ManifestError(SR.Format(SR.EventSource_ChannelTypeDoesNotMatchEventChannelValue,
5490 name, ((EventChannel)value).ToString()));
5493 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
5495 ulong kwd = GetChannelKeyword(chValue);
5497 if (channelTab == null)
5498 channelTab = new Dictionary<int, ChannelInfo>(4);
5499 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
5502 private EventChannelType EventChannelToChannelType(EventChannel channel)
5504 #if !ES_BUILD_STANDALONE
5505 Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
5506 #endif
5507 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
5509 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
5511 EventChannelAttribute attrib = new EventChannelAttribute();
5512 attrib.EventChannelType = EventChannelToChannelType(channel);
5513 if (attrib.EventChannelType <= EventChannelType.Operational)
5514 attrib.Enabled = true;
5515 return attrib;
5518 public ulong[] GetChannelData()
5520 if (this.channelTab == null)
5522 return Array.Empty<ulong>();
5525 // We create an array indexed by the channel id for fast look up.
5526 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
5527 int maxkey = -1;
5528 foreach (var item in this.channelTab.Keys)
5530 if (item > maxkey)
5532 maxkey = item;
5536 ulong[] channelMask = new ulong[maxkey + 1];
5537 foreach (var item in this.channelTab)
5539 channelMask[item.Key] = item.Value.Keywords;
5542 return channelMask;
5545 #endif
5546 public void StartEvent(string eventName, EventAttribute eventAttribute)
5548 Debug.Assert(numParams == 0);
5549 Debug.Assert(this.eventName == null);
5550 this.eventName = eventName;
5551 numParams = 0;
5552 byteArrArgIndices = null;
5554 events.Append(" <event").
5555 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
5556 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
5557 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
5558 Append(" symbol=\"").Append(eventName).Append("\"");
5560 // at this point we add to the manifest's stringTab a message that is as-of-yet
5561 // "untranslated to manifest convention", b/c we don't have the number or position
5562 // of any byte[] args (which require string format index updates)
5563 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
5565 if (eventAttribute.Keywords != 0)
5566 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
5567 if (eventAttribute.Opcode != 0)
5568 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
5569 if (eventAttribute.Task != 0)
5570 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
5571 #if FEATURE_MANAGED_ETW_CHANNELS
5572 if (eventAttribute.Channel != 0)
5574 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
5576 #endif
5579 public void AddEventParameter(Type type, string name)
5581 if (numParams == 0)
5582 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
5583 if (type == typeof(byte[]))
5585 // mark this index as "extraneous" (it has no parallel in the managed signature)
5586 // we use these values in TranslateToManifestConvention()
5587 if (byteArrArgIndices == null)
5588 byteArrArgIndices = new List<int>(4);
5589 byteArrArgIndices.Add(numParams);
5591 // add an extra field to the template representing the length of the binary blob
5592 numParams++;
5593 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
5595 numParams++;
5596 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
5597 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
5598 // as for 'byte[]' args (blob_arg_name + "Size")
5599 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
5601 // add "length" attribute to the "blob" field in the template (referencing the field added above)
5602 templates.Append(" length=\"").Append(name).Append("Size\"");
5604 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
5605 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(ulong) && Enum.GetUnderlyingType(type) != typeof(long))
5607 templates.Append(" map=\"").Append(type.Name).Append("\"");
5608 if (mapsTab == null)
5609 mapsTab = new Dictionary<string, Type>();
5610 if (!mapsTab.ContainsKey(type.Name))
5611 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
5614 templates.Append("/>").AppendLine();
5616 public void EndEvent()
5618 Debug.Assert(eventName != null);
5620 if (numParams > 0)
5622 templates.Append(" </template>").AppendLine();
5623 events.Append(" template=\"").Append(eventName).Append("Args\"");
5625 events.Append("/>").AppendLine();
5627 if (byteArrArgIndices != null)
5628 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
5630 // at this point we have all the information we need to translate the C# Message
5631 // to the manifest string we'll put in the stringTab
5632 if (stringTab.TryGetValue("event_" + eventName, out string? msg))
5634 msg = TranslateToManifestConvention(msg, eventName);
5635 stringTab["event_" + eventName] = msg;
5638 eventName = null;
5639 numParams = 0;
5640 byteArrArgIndices = null;
5643 #if FEATURE_MANAGED_ETW_CHANNELS
5644 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
5645 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
5646 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
5647 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
5648 // to channels by the OS infrastructure).
5649 // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
5650 // assumed that the keyword for that channel should be that bit.
5651 // otherwise we allocate a channel bit for the channel.
5652 // explicit channel bits are only used by WCF to mimic an existing manifest,
5653 // so we don't dont do error checking.
5654 public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
5656 // strip off any non-channel keywords, since we are only interested in channels here.
5657 channelKeyword &= ValidPredefinedChannelKeywords;
5658 if (channelTab == null)
5660 channelTab = new Dictionary<int, ChannelInfo>(4);
5663 if (channelTab.Count == MaxCountChannels)
5664 ManifestError(SR.EventSource_MaxChannelExceeded);
5666 ChannelInfo? info;
5667 if (!channelTab.TryGetValue((int)channel, out info))
5669 // If we were not given an explicit channel, allocate one.
5670 if (channelKeyword != 0)
5672 channelKeyword = nextChannelKeywordBit;
5673 nextChannelKeywordBit >>= 1;
5676 else
5678 channelKeyword = info.Keywords;
5681 return channelKeyword;
5683 #endif
5685 public byte[] CreateManifest()
5687 string str = CreateManifestString();
5688 return Encoding.UTF8.GetBytes(str);
5691 public IList<string> Errors { get { return errors; } }
5693 /// <summary>
5694 /// When validating an event source it adds the error to the error collection.
5695 /// When not validating it throws an exception if runtimeCritical is "true".
5696 /// Otherwise the error is ignored.
5697 /// </summary>
5698 /// <param name="msg"></param>
5699 /// <param name="runtimeCritical"></param>
5700 public void ManifestError(string msg, bool runtimeCritical = false)
5702 if ((flags & EventManifestOptions.Strict) != 0)
5703 errors.Add(msg);
5704 else if (runtimeCritical)
5705 throw new ArgumentException(msg);
5708 private string CreateManifestString()
5711 #if FEATURE_MANAGED_ETW_CHANNELS
5712 // Write out the channels
5713 if (channelTab != null)
5715 sb.Append(" <channels>").AppendLine();
5716 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
5717 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
5718 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
5719 foreach (var kvpair in sortedChannels)
5721 int channel = kvpair.Key;
5722 ChannelInfo channelInfo = kvpair.Value;
5724 string? channelType = null;
5725 string elementName = "channel";
5726 bool enabled = false;
5727 string? fullName = null;
5728 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5729 string? isolation = null;
5730 string? access = null;
5731 #endif
5732 if (channelInfo.Attribs != null)
5734 var attribs = channelInfo.Attribs;
5735 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
5736 channelType = attribs.EventChannelType.ToString();
5737 enabled = attribs.Enabled;
5738 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5739 if (attribs.ImportChannel != null)
5741 fullName = attribs.ImportChannel;
5742 elementName = "importChannel";
5744 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
5745 isolation = attribs.Isolation.ToString();
5746 access = attribs.Access;
5747 #endif
5749 if (fullName == null)
5750 fullName = providerName + "/" + channelInfo.Name;
5752 sb.Append(" <").Append(elementName);
5753 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
5754 sb.Append(" name=\"").Append(fullName).Append("\"");
5755 if (elementName == "channel") // not applicable to importChannels.
5757 Debug.Assert(channelInfo.Name != null);
5758 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
5759 sb.Append(" value=\"").Append(channel).Append("\"");
5760 if (channelType != null)
5761 sb.Append(" type=\"").Append(channelType).Append("\"");
5762 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
5763 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5764 if (access != null)
5765 sb.Append(" access=\"").Append(access).Append("\"");
5766 if (isolation != null)
5767 sb.Append(" isolation=\"").Append(isolation).Append("\"");
5768 #endif
5770 sb.Append("/>").AppendLine();
5772 sb.Append(" </channels>").AppendLine();
5774 #endif
5776 // Write out the tasks
5777 if (taskTab != null)
5780 sb.Append(" <tasks>").AppendLine();
5781 var sortedTasks = new List<int>(taskTab.Keys);
5782 sortedTasks.Sort();
5783 foreach (int task in sortedTasks)
5785 sb.Append(" <task");
5786 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
5787 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
5789 sb.Append(" </tasks>").AppendLine();
5792 // Write out the maps
5793 if (mapsTab != null)
5795 sb.Append(" <maps>").AppendLine();
5796 foreach (Type enumType in mapsTab.Values)
5798 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
5799 string mapKind = isbitmap ? "bitMap" : "valueMap";
5800 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
5802 // write out each enum value
5803 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
5804 bool anyValuesWritten = false;
5805 foreach (FieldInfo staticField in staticFields)
5807 object? constantValObj = staticField.GetRawConstantValue();
5809 if (constantValObj != null)
5811 ulong hexValue;
5812 if (constantValObj is ulong)
5813 hexValue = (ulong)constantValObj; // This is the only integer type that can't be represented by a long.
5814 else
5815 hexValue = (ulong) Convert.ToInt64(constantValObj); // Handles all integer types except ulong.
5817 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
5818 // TODO: Warn people about the dropping of values.
5819 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
5820 continue;
5821 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
5822 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
5823 sb.Append("/>").AppendLine();
5824 anyValuesWritten = true;
5828 // the OS requires that bitmaps and valuemaps have at least one value or it reject the whole manifest.
5829 // To avoid that put a 'None' entry if there are no other values.
5830 if (!anyValuesWritten)
5832 sb.Append(" <map value=\"0x0\"");
5833 WriteMessageAttrib(sb, "map", enumType.Name + "." + "None", "None");
5834 sb.Append("/>").AppendLine();
5836 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
5838 sb.Append(" </maps>").AppendLine();
5841 // Write out the opcodes
5842 sb.Append(" <opcodes>").AppendLine();
5843 var sortedOpcodes = new List<int>(opcodeTab.Keys);
5844 sortedOpcodes.Sort();
5845 foreach (int opcode in sortedOpcodes)
5847 sb.Append(" <opcode");
5848 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
5849 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
5851 sb.Append(" </opcodes>").AppendLine();
5853 // Write out the keywords
5854 if (keywordTab != null)
5856 sb.Append(" <keywords>").AppendLine();
5857 var sortedKeywords = new List<ulong>(keywordTab.Keys);
5858 sortedKeywords.Sort();
5859 foreach (ulong keyword in sortedKeywords)
5861 sb.Append(" <keyword");
5862 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
5863 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
5865 sb.Append(" </keywords>").AppendLine();
5868 sb.Append(" <events>").AppendLine();
5869 sb.Append(events);
5870 sb.Append(" </events>").AppendLine();
5872 sb.Append(" <templates>").AppendLine();
5873 if (templates.Length > 0)
5875 sb.Append(templates);
5877 else
5879 // Work around a cornercase ETW issue where a manifest with no templates causes
5880 // ETW events to not get sent to their associated channel.
5881 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
5883 sb.Append(" </templates>").AppendLine();
5885 sb.Append("</provider>").AppendLine();
5886 sb.Append("</events>").AppendLine();
5887 sb.Append("</instrumentation>").AppendLine();
5889 // Output the localization information.
5890 sb.Append("<localization>").AppendLine();
5892 List<CultureInfo>? cultures = null;
5893 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
5895 cultures = GetSupportedCultures(resources);
5897 else
5899 cultures = new List<CultureInfo>();
5900 cultures.Add(CultureInfo.CurrentUICulture);
5903 var sortedStrings = new string[stringTab.Keys.Count];
5904 stringTab.Keys.CopyTo(sortedStrings, 0);
5905 Array.Sort<string>(sortedStrings, 0, sortedStrings.Length);
5907 foreach (var ci in cultures)
5909 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
5910 sb.Append(" <stringTable>").AppendLine();
5912 foreach (var stringKey in sortedStrings)
5914 string? val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
5915 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
5917 sb.Append(" </stringTable>").AppendLine();
5918 sb.Append(" </resources>").AppendLine();
5920 sb.Append("</localization>").AppendLine();
5921 sb.AppendLine("</instrumentationManifest>");
5922 return sb.ToString();
5925 #region private
5926 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
5928 stringBuilder.Append(" name=\"").Append(name).Append("\"");
5929 WriteMessageAttrib(sb, elementName, name, name);
5931 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string? value)
5933 string key = elementName + "_" + name;
5934 // See if the user wants things localized.
5935 if (resources != null)
5937 // resource fallback: strings in the neutral culture will take precedence over inline strings
5938 string? localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
5939 if (localizedString != null)
5940 value = localizedString;
5942 if (value == null)
5943 return;
5945 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
5947 if (stringTab.TryGetValue(key, out string? prevValue) && !prevValue.Equals(value))
5949 ManifestError(SR.Format(SR.EventSource_DuplicateStringKey, key), true);
5950 return;
5953 stringTab[key] = value;
5955 internal string? GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
5957 string? value = null;
5958 if (resources != null)
5960 string? localizedString = resources.GetString(key, ci);
5961 if (localizedString != null)
5963 value = localizedString;
5964 if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
5966 var evtName = key.Substring("event_".Length);
5967 value = TranslateToManifestConvention(value, evtName);
5971 if (etwFormat && value == null)
5972 stringTab.TryGetValue(key, out value);
5974 return value;
5977 /// <summary>
5978 /// There's no API to enumerate all languages an assembly is localized into, so instead
5979 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
5980 /// assembly
5981 /// </summary>
5982 /// <param name="resources"></param>
5983 /// <returns></returns>
5984 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
5986 var cultures = new List<CultureInfo>();
5988 if (!cultures.Contains(CultureInfo.CurrentUICulture))
5989 cultures.Insert(0, CultureInfo.CurrentUICulture);
5990 return cultures;
5993 private static string GetLevelName(EventLevel level)
5995 return (((int)level >= 16) ? "" : "win:") + level.ToString();
5998 #if FEATURE_MANAGED_ETW_CHANNELS
5999 private string? GetChannelName(EventChannel channel, string eventName, string? eventMessage)
6001 ChannelInfo? info = null;
6002 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6004 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6005 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
6007 // allow channels to be auto-defined. The well known ones get their well known names, and the
6008 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
6009 if (channelTab == null)
6010 channelTab = new Dictionary<int, ChannelInfo>(4);
6012 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
6013 if (EventChannel.Debug < channel)
6014 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
6016 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6017 if (!channelTab.TryGetValue((int)channel, out info))
6018 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
6020 // events that specify admin channels *must* have non-null "Message" attributes
6021 if (resources != null && eventMessage == null)
6022 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6024 Debug.Assert(info!.Attribs != null);
6025 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6026 ManifestError(SR.Format(SR.EventSource_EventWithAdminChannelMustHaveMessage, eventName, info.Name));
6027 return info.Name;
6029 #endif
6030 private string GetTaskName(EventTask task, string eventName)
6032 if (task == EventTask.None)
6033 return "";
6035 string? ret;
6036 if (taskTab == null)
6037 taskTab = new Dictionary<int, string>();
6038 if (!taskTab.TryGetValue((int)task, out ret))
6039 ret = taskTab[(int)task] = eventName;
6040 return ret;
6043 private string? GetOpcodeName(EventOpcode opcode, string eventName)
6045 switch (opcode)
6047 case EventOpcode.Info:
6048 return "win:Info";
6049 case EventOpcode.Start:
6050 return "win:Start";
6051 case EventOpcode.Stop:
6052 return "win:Stop";
6053 case EventOpcode.DataCollectionStart:
6054 return "win:DC_Start";
6055 case EventOpcode.DataCollectionStop:
6056 return "win:DC_Stop";
6057 case EventOpcode.Extension:
6058 return "win:Extension";
6059 case EventOpcode.Reply:
6060 return "win:Reply";
6061 case EventOpcode.Resume:
6062 return "win:Resume";
6063 case EventOpcode.Suspend:
6064 return "win:Suspend";
6065 case EventOpcode.Send:
6066 return "win:Send";
6067 case EventOpcode.Receive:
6068 return "win:Receive";
6071 string? ret;
6072 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6074 ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true);
6075 ret = null;
6078 return ret;
6081 private string GetKeywords(ulong keywords, string eventName)
6083 #if FEATURE_MANAGED_ETW_CHANNELS
6084 // ignore keywords associate with channels
6085 // See ValidPredefinedChannelKeywords def for more.
6086 keywords &= ~ValidPredefinedChannelKeywords;
6087 #endif
6089 string ret = "";
6090 for (ulong bit = 1; bit != 0; bit <<= 1)
6092 if ((keywords & bit) != 0)
6094 string? keyword = null;
6095 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6096 (bit >= (ulong)0x1000000000000))
6098 // do not report Windows reserved keywords in the manifest (this allows the code
6099 // to be resilient to potential renaming of these keywords)
6100 keyword = string.Empty;
6102 if (keyword == null)
6104 ManifestError(SR.Format(SR.EventSource_UndefinedKeyword, "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6105 keyword = string.Empty;
6107 if (ret.Length != 0 && keyword.Length != 0)
6108 ret = ret + " ";
6109 ret = ret + keyword;
6112 return ret;
6115 private string GetTypeName(Type type)
6117 if (type.IsEnum())
6119 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6120 var typeName = GetTypeName(fields[0].FieldType);
6121 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6124 switch (type.GetTypeCode())
6126 case TypeCode.Boolean:
6127 return "win:Boolean";
6128 case TypeCode.Byte:
6129 return "win:UInt8";
6130 case TypeCode.Char:
6131 case TypeCode.UInt16:
6132 return "win:UInt16";
6133 case TypeCode.UInt32:
6134 return "win:UInt32";
6135 case TypeCode.UInt64:
6136 return "win:UInt64";
6137 case TypeCode.SByte:
6138 return "win:Int8";
6139 case TypeCode.Int16:
6140 return "win:Int16";
6141 case TypeCode.Int32:
6142 return "win:Int32";
6143 case TypeCode.Int64:
6144 return "win:Int64";
6145 case TypeCode.String:
6146 return "win:UnicodeString";
6147 case TypeCode.Single:
6148 return "win:Float";
6149 case TypeCode.Double:
6150 return "win:Double";
6151 case TypeCode.DateTime:
6152 return "win:FILETIME";
6153 default:
6154 if (type == typeof(Guid))
6155 return "win:GUID";
6156 else if (type == typeof(IntPtr))
6157 return "win:Pointer";
6158 else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6159 return "win:Binary";
6161 ManifestError(SR.Format(SR.EventSource_UnsupportedEventTypeInManifest, type.Name), true);
6162 return string.Empty;
6166 private static void UpdateStringBuilder([NotNull] ref StringBuilder? stringBuilder, string eventMessage, int startIndex, int count)
6168 if (stringBuilder == null)
6169 stringBuilder = new StringBuilder();
6170 stringBuilder.Append(eventMessage, startIndex, count);
6173 private static readonly string[] s_escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
6174 // Manifest messages use %N conventions for their message substitutions. Translate from
6175 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6176 private string TranslateToManifestConvention(string eventMessage, string evtName)
6178 StringBuilder? stringBuilder = null; // We lazily create this
6179 int writtenSoFar = 0;
6180 int chIdx = -1;
6181 for (int i = 0; ;)
6183 if (i >= eventMessage.Length)
6185 if (stringBuilder == null)
6186 return eventMessage;
6187 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6188 return stringBuilder.ToString();
6191 if (eventMessage[i] == '%')
6193 // handle format message escaping character '%' by escaping it
6194 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6195 stringBuilder.Append("%%");
6196 i++;
6197 writtenSoFar = i;
6199 else if (i < eventMessage.Length - 1 &&
6200 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6202 // handle C# escaped '{" and '}'
6203 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6204 stringBuilder.Append(eventMessage[i]);
6205 i++; i++;
6206 writtenSoFar = i;
6208 else if (eventMessage[i] == '{')
6210 int leftBracket = i;
6211 i++;
6212 int argNum = 0;
6213 while (i < eventMessage.Length && char.IsDigit(eventMessage[i]))
6215 argNum = argNum * 10 + eventMessage[i] - '0';
6216 i++;
6218 if (i < eventMessage.Length && eventMessage[i] == '}')
6220 i++;
6221 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6222 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6223 stringBuilder.Append('%').Append(manIndex);
6224 // An '!' after the insert specifier {n} will be interpreted as a literal.
6225 // We'll escape it so that mc.exe does not attempt to consider it the
6226 // beginning of a format string.
6227 if (i < eventMessage.Length && eventMessage[i] == '!')
6229 i++;
6230 stringBuilder.Append("%!");
6232 writtenSoFar = i;
6234 else
6236 ManifestError(SR.Format(SR.EventSource_UnsupportedMessageProperty, evtName, eventMessage));
6239 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6241 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6242 i++;
6243 stringBuilder.Append(s_escapes[chIdx]);
6244 writtenSoFar = i;
6246 else
6247 i++;
6251 private int TranslateIndexToManifestConvention(int idx, string evtName)
6253 List<int>? byteArrArgIndices;
6254 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6256 foreach (var byArrIdx in byteArrArgIndices)
6258 if (idx >= byArrIdx)
6259 ++idx;
6260 else
6261 break;
6264 return idx + 1;
6267 #if FEATURE_MANAGED_ETW_CHANNELS
6268 class ChannelInfo
6270 public string? Name;
6271 public ulong Keywords;
6272 public EventChannelAttribute? Attribs;
6274 #endif
6276 Dictionary<int, string> opcodeTab;
6277 Dictionary<int, string>? taskTab;
6278 #if FEATURE_MANAGED_ETW_CHANNELS
6279 Dictionary<int, ChannelInfo>? channelTab;
6280 #endif
6281 Dictionary<ulong, string>? keywordTab;
6282 Dictionary<string, Type>? mapsTab;
6284 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6286 #if FEATURE_MANAGED_ETW_CHANNELS
6287 // WCF used EventSource to mimic a existing ETW manifest. To support this
6288 // in just their case, we allowed them to specify the keywords associated
6289 // with their channels explicitly. ValidPredefinedChannelKeywords is
6290 // this set of channel keywords that we allow to be explicitly set. You
6291 // can ignore these bits otherwise.
6292 internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
6293 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
6294 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6295 #endif
6297 StringBuilder sb; // Holds the provider information.
6298 StringBuilder events; // Holds the events.
6299 StringBuilder templates;
6301 #if FEATURE_MANAGED_ETW_CHANNELS
6302 string providerName;
6303 #endif
6304 ResourceManager? resources; // Look up localized strings here.
6305 EventManifestOptions flags;
6306 IList<string> errors; // list of currently encountered errors
6307 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6309 // State we track between StartEvent and EndEvent.
6310 string? eventName; // Name of the event currently being processed.
6311 int numParams; // keeps track of the number of args the event has.
6312 List<int>? byteArrArgIndices; // keeps track of the index of each byte[] argument
6313 #endregion
6316 /// <summary>
6317 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6318 /// </summary>
6319 internal struct ManifestEnvelope
6321 public const int MaxChunkSize = 0xFF00;
6322 public enum ManifestFormats : byte
6324 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6327 #if FEATURE_MANAGED_ETW
6328 public ManifestFormats Format;
6329 public byte MajorVersion;
6330 public byte MinorVersion;
6331 public byte Magic;
6332 public ushort TotalChunks;
6333 public ushort ChunkNumber;
6334 #endif
6337 #endregion