Fix IDE0025 (use expression body for properties)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Diagnostics / Tracing / EventSource.cs
blob8397670245785e90aea8f7599d84730c0de3986f
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
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:
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:
51 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
52 // during the transfer. In particular object identity is not preserved, some objects are morphed,
53 // and not all data types are supported. In particular you can pass
55 // A Valid type to log to an EventSource include
56 // * Primitive data types
57 // * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
58 // * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
60 // This set of types is roughly a generalization of JSON support (basically primitives, bags, and arrays).
62 // Explicitly allowed structs include (* New for V4.6)
63 // * Marked with the EventData attribute
64 // * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
65 // * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
67 // When classes are returned in an EventListener, what is returned is something that implements
68 // IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
69 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
70 // are obviously NOT the original objects.
72 // ETW serialization formats:
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
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.
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 #pragma warning disable CA1823 // field is used to keep listener alive
245 private static readonly EventListener? persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
246 #pragma warning restore CA1823
247 #endif //FEATURE_EVENTSOURCE_XPLAT
249 /// <summary>
250 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
251 /// </summary>
252 public string Name => m_name;
253 /// <summary>
254 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
255 /// </summary>
256 public Guid Guid => m_guid;
258 /// <summary>
259 /// Returns true if the eventSource has been enabled at all. This is the preferred test
260 /// to be performed before a relatively expensive EventSource operation.
261 /// </summary>
262 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
263 public bool IsEnabled()
265 return m_eventSourceEnabled;
268 /// <summary>
269 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
271 /// Note that the result of this function is only an approximation on whether a particular
272 /// event is active or not. It is only meant to be used as way of avoiding expensive
273 /// computation for logging when logging is not on, therefore it sometimes returns false
274 /// positives (but is always accurate when returning false). EventSources are free to
275 /// have additional filtering.
276 /// </summary>
277 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
278 public bool IsEnabled(EventLevel level, EventKeywords keywords)
280 return IsEnabled(level, keywords, EventChannel.None);
283 /// <summary>
284 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
285 /// if 'keywords' specifies a channel bit for a channel that is enabled.
287 /// Note that the result of this function only an approximation on whether a particular
288 /// event is active or not. It is only meant to be used as way of avoiding expensive
289 /// computation for logging when logging is not on, therefore it sometimes returns false
290 /// positives (but is always accurate when returning false). EventSources are free to
291 /// have additional filtering.
292 /// </summary>
293 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
294 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
296 if (!m_eventSourceEnabled)
297 return false;
299 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
300 return false;
302 return true;
305 /// <summary>
306 /// Returns the settings for the event source instance
307 /// </summary>
308 public EventSourceSettings Settings => m_config;
310 // Manifest support
311 /// <summary>
312 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
313 /// This API allows you to compute this without actually creating an instance of the EventSource.
314 /// It only needs to reflect over the type.
315 /// </summary>
316 public static Guid GetGuid(Type eventSourceType)
318 if (eventSourceType == null)
319 throw new ArgumentNullException(nameof(eventSourceType));
321 EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
322 string name = eventSourceType.Name;
323 if (attrib != null)
325 if (attrib.Guid != null)
327 Guid g = Guid.Empty;
328 #if !ES_BUILD_AGAINST_DOTNET_V35
329 if (Guid.TryParse(attrib.Guid, out g))
330 return g;
331 #else
332 try { return new Guid(attrib.Guid); }
333 catch (Exception) { }
334 #endif
337 if (attrib.Name != null)
338 name = attrib.Name;
341 if (name == null)
343 throw new ArgumentException(SR.Argument_InvalidTypeName, nameof(eventSourceType));
345 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
347 /// <summary>
348 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
349 /// This API allows you to compute this without actually creating an instance of the EventSource.
350 /// It only needs to reflect over the type.
351 /// </summary>
352 public static string GetName(Type eventSourceType)
354 return GetName(eventSourceType, EventManifestOptions.None);
357 /// <summary>
358 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
359 /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema.
360 /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
361 /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
362 /// </summary>
363 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
364 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
365 /// which it is embedded. This parameter specifies what name will be used</param>
366 /// <returns>The XML data string</returns>
367 public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest)
369 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
371 /// <summary>
372 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
373 /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema.
374 /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
375 /// ensures that the entries in the event log will be "optimally" localized.
376 /// </summary>
377 /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
378 /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
379 /// which it is embedded. This parameter specifies what name will be used</param>
380 /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
381 /// this returns null when the eventSourceType does not require explicit registration</param>
382 /// <returns>The XML data string or null</returns>
383 public static string? GenerateManifest(Type eventSourceType, string? assemblyPathToIncludeInManifest, EventManifestOptions flags)
385 if (eventSourceType == null)
386 throw new ArgumentNullException(nameof(eventSourceType));
388 byte[]? manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
389 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
392 // EventListener support
393 /// <summary>
394 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
395 /// </summary>
396 /// <returns></returns>
397 public static IEnumerable<EventSource> GetSources()
399 var ret = new List<EventSource>();
400 lock (EventListener.EventListenersLock)
402 Debug.Assert(EventListener.s_EventSources != null);
404 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
406 if (eventSourceRef.Target is EventSource eventSource && !eventSource.IsDisposed)
407 ret.Add(eventSource);
410 return ret;
413 /// <summary>
414 /// Send a command to a particular EventSource identified by 'eventSource'.
415 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
416 /// callback. What the EventSource does with the command and its arguments are from
417 /// that point EventSource-specific.
418 /// </summary>
419 /// <param name="eventSource">The instance of EventSource to send the command to</param>
420 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
421 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
422 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string?>? commandArguments)
424 if (eventSource == null)
425 throw new ArgumentNullException(nameof(eventSource));
427 // User-defined EventCommands should not conflict with the reserved commands.
428 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
430 throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command));
433 eventSource.SendCommand(null, EventProviderType.ETW, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
436 // Error APIs. (We don't throw by default, but you can probe for status)
437 /// <summary>
438 /// Because
440 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
441 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
443 /// The event source constructor does not throw exceptions. Instead we remember any exception that
444 /// was generated (it is also logged to Trace.WriteLine).
445 /// </summary>
446 public Exception? ConstructionException => m_constructionException;
448 /// <summary>
449 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
450 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
451 /// (e.g. like the built in ETW listener). These traits are specified at EventSource
452 /// construction time and can be retrieved by using this GetTrait API.
453 /// </summary>
454 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
455 /// <returns>The value string associated with key. Will return null if there is no such key.</returns>
456 public string? GetTrait(string key)
458 if (m_traits != null)
460 for (int i = 0; i < m_traits.Length - 1; i += 2)
462 if (m_traits[i] == key)
463 return m_traits[i + 1];
467 return null;
470 /// <summary>
471 /// Displays the name and GUID for the eventSource for debugging purposes.
472 /// </summary>
473 public override string ToString()
475 return SR.Format(SR.EventSource_ToString, Name, Guid);
478 /// <summary>
479 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
480 /// </summary>
481 public event EventHandler<EventCommandEventArgs>? EventCommandExecuted
485 if (value == null)
486 return;
488 m_eventCommandExecuted += value;
490 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
491 // It should get a chance to handle the deferred commands.
492 EventCommandEventArgs? deferredCommands = m_deferredCommands;
493 while (deferredCommands != null)
495 value(this, deferredCommands);
496 deferredCommands = deferredCommands.nextCommand;
499 remove
501 m_eventCommandExecuted -= value;
505 #region ActivityID
507 /// <summary>
508 /// When a thread starts work that is on behalf of 'something else' (typically another
509 /// thread or network request) it should mark the thread as working on that other work.
510 /// This API marks the current thread as working on activity 'activityID'. This API
511 /// should be used when the caller knows the thread's current activity (the one being
512 /// overwritten) has completed. Otherwise, callers should prefer the overload that
513 /// return the oldActivityThatWillContinue (below).
515 /// All events created with the EventSource on this thread are also tagged with the
516 /// activity ID of the thread.
518 /// It is common, and good practice after setting the thread to an activity to log an event
519 /// with a 'start' opcode to indicate that precise time/thread where the new activity
520 /// started.
521 /// </summary>
522 /// <param name="activityId">A Guid that represents the new activity with which to mark
523 /// the current thread</param>
524 public static void SetCurrentThreadActivityId(Guid activityId)
526 if (TplEventSource.Log != null)
527 TplEventSource.Log.SetActivityId(activityId);
529 // We ignore errors to keep with the convention that EventSources do not throw errors.
530 // Note we can't access m_throwOnWrites because this is a static method.
531 #if FEATURE_MANAGED_ETW
532 #if FEATURE_PERFTRACING
533 // Set the activity id via EventPipe.
534 EventPipeInternal.EventActivityIdControl(
535 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
536 ref activityId);
537 #endif // FEATURE_PERFTRACING
538 #if PLATFORM_WINDOWS
539 // Set the activity id via ETW.
540 Interop.Advapi32.EventActivityIdControl(
541 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
542 ref activityId);
543 #endif // PLATFORM_WINDOWS
544 #endif // FEATURE_MANAGED_ETW
547 /// <summary>
548 /// Retrieves the ETW activity ID associated with the current thread.
549 /// </summary>
550 public static Guid CurrentThreadActivityId
554 // We ignore errors to keep with the convention that EventSources do not throw
555 // errors. Note we can't access m_throwOnWrites because this is a static method.
556 Guid retVal = new Guid();
557 #if FEATURE_MANAGED_ETW
558 #if PLATFORM_WINDOWS
559 Interop.Advapi32.EventActivityIdControl(
560 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
561 ref retVal);
562 #elif FEATURE_PERFTRACING
563 EventPipeInternal.EventActivityIdControl(
564 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
565 ref retVal);
566 #endif // PLATFORM_WINDOWS
567 #endif // FEATURE_MANAGED_ETW
568 return retVal;
572 /// <summary>
573 /// When a thread starts work that is on behalf of 'something else' (typically another
574 /// thread or network request) it should mark the thread as working on that other work.
575 /// This API marks the current thread as working on activity 'activityID'. It returns
576 /// whatever activity the thread was previously marked with. There is a convention that
577 /// callers can assume that callees restore this activity mark before the callee returns.
578 /// To encourage this, this API returns the old activity, so that it can be restored later.
580 /// All events created with the EventSource on this thread are also tagged with the
581 /// activity ID of the thread.
583 /// It is common, and good practice after setting the thread to an activity to log an event
584 /// with a 'start' opcode to indicate that precise time/thread where the new activity
585 /// started.
586 /// </summary>
587 /// <param name="activityId">A Guid that represents the new activity with which to mark
588 /// the current thread</param>
589 /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
590 /// which will continue at some point in the future, on the current thread</param>
591 public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
593 oldActivityThatWillContinue = activityId;
594 #if FEATURE_MANAGED_ETW
595 // We ignore errors to keep with the convention that EventSources do not throw errors.
596 // Note we can't access m_throwOnWrites because this is a static method.
598 #if FEATURE_PERFTRACING && PLATFORM_WINDOWS
599 EventPipeInternal.EventActivityIdControl(
600 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
601 ref oldActivityThatWillContinue);
602 #elif FEATURE_PERFTRACING
603 EventPipeInternal.EventActivityIdControl(
604 (uint)Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
605 ref oldActivityThatWillContinue);
606 #endif // FEATURE_PERFTRACING && PLATFORM_WINDOWS
608 #if PLATFORM_WINDOWS
609 Interop.Advapi32.EventActivityIdControl(
610 Interop.Advapi32.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
611 ref oldActivityThatWillContinue);
612 #endif // PLATFORM_WINDOWS
613 #endif // FEATURE_MANAGED_ETW
615 // We don't call the activityDying callback here because the caller has declared that
616 // it is not dying.
617 if (TplEventSource.Log != null)
618 TplEventSource.Log.SetActivityId(activityId);
620 #endregion
622 #region protected
623 /// <summary>
624 /// This is the constructor that most users will use to create their eventSource. It takes
625 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
626 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
627 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
628 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
629 /// the ETW provider name.
630 /// </summary>
631 protected EventSource()
632 : this(EventSourceSettings.EtwManifestEventFormat)
636 /// <summary>
637 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
638 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
639 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
640 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
641 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
642 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
644 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
645 /// </summary>
646 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
647 protected EventSource(bool throwOnEventWriteErrors)
648 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
651 /// <summary>
652 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
653 /// </summary>
654 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
656 /// <summary>
657 /// Construct an EventSource with additional non-default settings.
659 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
660 /// The first string is the key and the second is the value. These are not interpreted by EventSource
661 /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string).
662 /// </summary>
663 /// <param name="settings">See EventSourceSettings for more.</param>
664 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
665 protected EventSource(EventSourceSettings settings, params string[]? traits)
667 m_config = ValidateSettings(settings);
669 Guid eventSourceGuid;
670 string? eventSourceName;
672 GetMetadata(out eventSourceGuid, out eventSourceName, out _, out _);
674 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
676 Type myType = this.GetType();
677 eventSourceGuid = GetGuid(myType);
678 eventSourceName = GetName(myType);
681 Initialize(eventSourceGuid, eventSourceName, traits);
684 #if FEATURE_PERFTRACING
685 // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly
686 // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere.
687 private unsafe void DefineEventPipeEvents()
689 // If the EventSource is set to emit all events as TraceLogging events, skip this initialization.
690 // Events will be defined when they are emitted for the first time.
691 if (SelfDescribingEvents)
693 return;
696 Debug.Assert(m_eventData != null);
697 Debug.Assert(m_eventPipeProvider != null);
698 int cnt = m_eventData.Length;
699 for (int i = 0; i < cnt; i++)
701 uint eventID = (uint)m_eventData[i].Descriptor.EventId;
702 if (eventID == 0)
703 continue;
705 byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateEventMetadata(m_eventData[i]);
706 uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
708 string eventName = m_eventData[i].Name;
709 long keywords = m_eventData[i].Descriptor.Keywords;
710 uint eventVersion = m_eventData[i].Descriptor.Version;
711 uint level = m_eventData[i].Descriptor.Level;
713 fixed (byte *pMetadata = metadata)
715 IntPtr eventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(
716 eventID,
717 eventName,
718 keywords,
719 eventVersion,
720 level,
721 pMetadata,
722 metadataLength);
724 Debug.Assert(eventHandle != IntPtr.Zero);
725 m_eventData[i].EventHandle = eventHandle;
729 #endif
731 internal virtual void GetMetadata(out Guid eventSourceGuid, out string? eventSourceName, out EventMetadata[]? eventData, out byte[]? manifestBytes)
734 // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
735 // On other architectures it is a no-op.
737 // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
738 // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
740 // 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.
742 eventSourceGuid = Guid.Empty;
743 eventSourceName = null;
744 eventData = null;
745 manifestBytes = null;
747 return;
750 /// <summary>
751 /// This method is called when the eventSource is updated by the controller.
752 /// </summary>
753 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
755 #pragma warning disable 1591
756 // optimized for common signatures (no args)
757 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
758 protected unsafe void WriteEvent(int eventId)
760 WriteEventCore(eventId, 0, null);
763 // optimized for common signatures (ints)
764 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
765 protected unsafe void WriteEvent(int eventId, int arg1)
767 if (m_eventSourceEnabled)
769 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
770 descrs[0].DataPointer = (IntPtr)(&arg1);
771 descrs[0].Size = 4;
772 descrs[0].Reserved = 0;
773 WriteEventCore(eventId, 1, descrs);
777 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
778 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
780 if (m_eventSourceEnabled)
782 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
783 descrs[0].DataPointer = (IntPtr)(&arg1);
784 descrs[0].Size = 4;
785 descrs[0].Reserved = 0;
786 descrs[1].DataPointer = (IntPtr)(&arg2);
787 descrs[1].Size = 4;
788 descrs[1].Reserved = 0;
789 WriteEventCore(eventId, 2, descrs);
793 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
794 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
796 if (m_eventSourceEnabled)
798 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
799 descrs[0].DataPointer = (IntPtr)(&arg1);
800 descrs[0].Size = 4;
801 descrs[0].Reserved = 0;
802 descrs[1].DataPointer = (IntPtr)(&arg2);
803 descrs[1].Size = 4;
804 descrs[1].Reserved = 0;
805 descrs[2].DataPointer = (IntPtr)(&arg3);
806 descrs[2].Size = 4;
807 descrs[2].Reserved = 0;
808 WriteEventCore(eventId, 3, descrs);
812 // optimized for common signatures (longs)
813 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
814 protected unsafe void WriteEvent(int eventId, long arg1)
816 if (m_eventSourceEnabled)
818 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
819 descrs[0].DataPointer = (IntPtr)(&arg1);
820 descrs[0].Size = 8;
821 descrs[0].Reserved = 0;
822 WriteEventCore(eventId, 1, descrs);
826 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
827 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
829 if (m_eventSourceEnabled)
831 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
832 descrs[0].DataPointer = (IntPtr)(&arg1);
833 descrs[0].Size = 8;
834 descrs[0].Reserved = 0;
835 descrs[1].DataPointer = (IntPtr)(&arg2);
836 descrs[1].Size = 8;
837 descrs[1].Reserved = 0;
838 WriteEventCore(eventId, 2, descrs);
842 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
843 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
845 if (m_eventSourceEnabled)
847 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
848 descrs[0].DataPointer = (IntPtr)(&arg1);
849 descrs[0].Size = 8;
850 descrs[0].Reserved = 0;
851 descrs[1].DataPointer = (IntPtr)(&arg2);
852 descrs[1].Size = 8;
853 descrs[1].Reserved = 0;
854 descrs[2].DataPointer = (IntPtr)(&arg3);
855 descrs[2].Size = 8;
856 descrs[2].Reserved = 0;
857 WriteEventCore(eventId, 3, descrs);
861 // optimized for common signatures (strings)
862 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
863 protected unsafe void WriteEvent(int eventId, string? arg1)
865 if (m_eventSourceEnabled)
867 if (arg1 == null) arg1 = "";
868 fixed (char* string1Bytes = arg1)
870 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
871 descrs[0].DataPointer = (IntPtr)string1Bytes;
872 descrs[0].Size = ((arg1.Length + 1) * 2);
873 descrs[0].Reserved = 0;
874 WriteEventCore(eventId, 1, descrs);
879 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
880 protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2)
882 if (m_eventSourceEnabled)
884 if (arg1 == null) arg1 = "";
885 if (arg2 == null) arg2 = "";
886 fixed (char* string1Bytes = arg1)
887 fixed (char* string2Bytes = arg2)
889 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
890 descrs[0].DataPointer = (IntPtr)string1Bytes;
891 descrs[0].Size = ((arg1.Length + 1) * 2);
892 descrs[0].Reserved = 0;
893 descrs[1].DataPointer = (IntPtr)string2Bytes;
894 descrs[1].Size = ((arg2.Length + 1) * 2);
895 descrs[1].Reserved = 0;
896 WriteEventCore(eventId, 2, descrs);
901 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
902 protected unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3)
904 if (m_eventSourceEnabled)
906 if (arg1 == null) arg1 = "";
907 if (arg2 == null) arg2 = "";
908 if (arg3 == null) arg3 = "";
909 fixed (char* string1Bytes = arg1)
910 fixed (char* string2Bytes = arg2)
911 fixed (char* string3Bytes = arg3)
913 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
914 descrs[0].DataPointer = (IntPtr)string1Bytes;
915 descrs[0].Size = ((arg1.Length + 1) * 2);
916 descrs[0].Reserved = 0;
917 descrs[1].DataPointer = (IntPtr)string2Bytes;
918 descrs[1].Size = ((arg2.Length + 1) * 2);
919 descrs[1].Reserved = 0;
920 descrs[2].DataPointer = (IntPtr)string3Bytes;
921 descrs[2].Size = ((arg3.Length + 1) * 2);
922 descrs[2].Reserved = 0;
923 WriteEventCore(eventId, 3, descrs);
928 // optimized for common signatures (string and ints)
929 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
930 protected unsafe void WriteEvent(int eventId, string? arg1, int arg2)
932 if (m_eventSourceEnabled)
934 if (arg1 == null) arg1 = "";
935 fixed (char* string1Bytes = arg1)
937 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
938 descrs[0].DataPointer = (IntPtr)string1Bytes;
939 descrs[0].Size = ((arg1.Length + 1) * 2);
940 descrs[0].Reserved = 0;
941 descrs[1].DataPointer = (IntPtr)(&arg2);
942 descrs[1].Size = 4;
943 descrs[1].Reserved = 0;
944 WriteEventCore(eventId, 2, descrs);
949 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
950 protected unsafe void WriteEvent(int eventId, string? arg1, int arg2, int arg3)
952 if (m_eventSourceEnabled)
954 if (arg1 == null) arg1 = "";
955 fixed (char* string1Bytes = arg1)
957 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
958 descrs[0].DataPointer = (IntPtr)string1Bytes;
959 descrs[0].Size = ((arg1.Length + 1) * 2);
960 descrs[0].Reserved = 0;
961 descrs[1].DataPointer = (IntPtr)(&arg2);
962 descrs[1].Size = 4;
963 descrs[1].Reserved = 0;
964 descrs[2].DataPointer = (IntPtr)(&arg3);
965 descrs[2].Size = 4;
966 descrs[2].Reserved = 0;
967 WriteEventCore(eventId, 3, descrs);
972 // optimized for common signatures (string and longs)
973 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
974 protected unsafe void WriteEvent(int eventId, string? arg1, long arg2)
976 if (m_eventSourceEnabled)
978 if (arg1 == null) arg1 = "";
979 fixed (char* string1Bytes = arg1)
981 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
982 descrs[0].DataPointer = (IntPtr)string1Bytes;
983 descrs[0].Size = ((arg1.Length + 1) * 2);
984 descrs[0].Reserved = 0;
985 descrs[1].DataPointer = (IntPtr)(&arg2);
986 descrs[1].Size = 8;
987 descrs[1].Reserved = 0;
988 WriteEventCore(eventId, 2, descrs);
993 // optimized for common signatures (long and string)
994 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
995 protected unsafe void WriteEvent(int eventId, long arg1, string? arg2)
997 if (m_eventSourceEnabled)
999 if (arg2 == null) arg2 = "";
1000 fixed (char* string2Bytes = arg2)
1002 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1003 descrs[0].DataPointer = (IntPtr)(&arg1);
1004 descrs[0].Size = 8;
1005 descrs[0].Reserved = 0;
1006 descrs[1].DataPointer = (IntPtr)string2Bytes;
1007 descrs[1].Size = ((arg2.Length + 1) * 2);
1008 descrs[1].Reserved = 0;
1009 WriteEventCore(eventId, 2, descrs);
1014 // optimized for common signatures (int and string)
1015 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1016 protected unsafe void WriteEvent(int eventId, int arg1, string? arg2)
1018 if (m_eventSourceEnabled)
1020 if (arg2 == null) arg2 = "";
1021 fixed (char* string2Bytes = arg2)
1023 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1024 descrs[0].DataPointer = (IntPtr)(&arg1);
1025 descrs[0].Size = 4;
1026 descrs[0].Reserved = 0;
1027 descrs[1].DataPointer = (IntPtr)string2Bytes;
1028 descrs[1].Size = ((arg2.Length + 1) * 2);
1029 descrs[1].Reserved = 0;
1030 WriteEventCore(eventId, 2, descrs);
1035 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1036 protected unsafe void WriteEvent(int eventId, byte[]? arg1)
1038 if (m_eventSourceEnabled)
1040 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1041 if (arg1 == null || arg1.Length == 0)
1043 int blobSize = 0;
1044 descrs[0].DataPointer = (IntPtr)(&blobSize);
1045 descrs[0].Size = 4;
1046 descrs[0].Reserved = 0;
1047 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
1048 descrs[1].Size = 0;
1049 descrs[1].Reserved = 0;
1050 WriteEventCore(eventId, 2, descrs);
1052 else
1054 int blobSize = arg1.Length;
1055 fixed (byte* blob = &arg1[0])
1057 descrs[0].DataPointer = (IntPtr)(&blobSize);
1058 descrs[0].Size = 4;
1059 descrs[0].Reserved = 0;
1060 descrs[1].DataPointer = (IntPtr)blob;
1061 descrs[1].Size = blobSize;
1062 descrs[1].Reserved = 0;
1063 WriteEventCore(eventId, 2, descrs);
1069 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1070 protected unsafe void WriteEvent(int eventId, long arg1, byte[]? arg2)
1072 if (m_eventSourceEnabled)
1074 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1075 descrs[0].DataPointer = (IntPtr)(&arg1);
1076 descrs[0].Size = 8;
1077 descrs[0].Reserved = 0;
1078 if (arg2 == null || arg2.Length == 0)
1080 int blobSize = 0;
1081 descrs[1].DataPointer = (IntPtr)(&blobSize);
1082 descrs[1].Size = 4;
1083 descrs[1].Reserved = 0;
1084 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1085 descrs[2].Size = 0;
1086 descrs[2].Reserved = 0;
1087 WriteEventCore(eventId, 3, descrs);
1089 else
1091 int blobSize = arg2.Length;
1092 fixed (byte* blob = &arg2[0])
1094 descrs[1].DataPointer = (IntPtr)(&blobSize);
1095 descrs[1].Size = 4;
1096 descrs[1].Reserved = 0;
1097 descrs[2].DataPointer = (IntPtr)blob;
1098 descrs[2].Size = blobSize;
1099 descrs[2].Reserved = 0;
1100 WriteEventCore(eventId, 3, descrs);
1106 #pragma warning restore 1591
1108 /// <summary>
1109 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1110 /// </summary>
1111 protected internal struct EventData
1113 /// <summary>
1114 /// Address where the one argument lives (if this points to managed memory you must ensure the
1115 /// managed object is pinned.
1116 /// </summary>
1117 public unsafe IntPtr DataPointer { get { return (IntPtr)(void*)m_Ptr; } set { m_Ptr = unchecked((ulong)(void*)value); } }
1119 /// <summary>
1120 /// Size of the argument referenced by DataPointer
1121 /// </summary>
1122 public int Size { get { return m_Size; } set { m_Size = value; } }
1124 /// <summary>
1125 /// Reserved by ETW. This property is present to ensure that we can zero it
1126 /// since System.Private.CoreLib uses are not zero'd.
1127 /// </summary>
1128 internal int Reserved { get { return m_Reserved; } set { m_Reserved = value; } }
1130 #region private
1131 /// <summary>
1132 /// Initializes the members of this EventData object to point at a previously-pinned
1133 /// tracelogging-compatible metadata blob.
1134 /// </summary>
1135 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1136 /// <param name="size">The size of the metadata blob.</param>
1137 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1138 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1140 this.m_Ptr = (ulong)pointer;
1141 this.m_Size = size;
1142 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1145 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1146 // the way EventWrite wants it.
1147 internal ulong m_Ptr;
1148 internal int m_Size;
1149 #pragma warning disable 0649
1150 internal int m_Reserved; // Used to pad the size to match the Win32 API
1151 #pragma warning restore 0649
1152 #endregion
1155 /// <summary>
1156 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1157 /// do this, while straightforward, is unsafe.
1158 /// </summary>
1159 /// <remarks>
1160 /// <code>
1161 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1162 /// {
1163 /// if (IsEnabled())
1164 /// {
1165 /// if (arg2 == null) arg2 = "";
1166 /// fixed (char* string2Bytes = arg2)
1167 /// {
1168 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1169 /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1170 /// descrs[0].Size = 8;
1171 /// descrs[0].Reserved = 0;
1172 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1173 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1174 /// descrs[1].Reserved = 0;
1175 /// WriteEventCore(eventId, 2, descrs);
1176 /// }
1177 /// }
1178 /// }
1179 /// </code>
1180 /// </remarks>
1181 [CLSCompliant(false)]
1182 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1184 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1187 /// <summary>
1188 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1189 /// that you use to do this, while straightforward, is unsafe. The only difference from
1190 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1191 /// </summary>
1192 /// <remarks>
1193 /// <code>
1194 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1195 /// {
1196 /// if (IsEnabled())
1197 /// {
1198 /// if (arg2 == null) arg2 = "";
1199 /// fixed (char* string2Bytes = arg2)
1200 /// {
1201 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1202 /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1203 /// descrs[0].Size = 8;
1204 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1205 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1206 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1207 /// }
1208 /// }
1209 /// }
1210 /// </code>
1211 /// </remarks>
1212 [CLSCompliant(false)]
1213 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1215 if (m_eventSourceEnabled)
1217 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1220 if (relatedActivityId != null)
1221 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1223 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1224 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1225 Guid* pActivityId = null;
1226 Guid activityId = Guid.Empty;
1227 Guid relActivityId = Guid.Empty;
1229 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1230 ((activityOptions & EventActivityOptions.Disable) == 0))
1232 if (opcode == EventOpcode.Start)
1234 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1236 else if (opcode == EventOpcode.Stop)
1238 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1241 if (activityId != Guid.Empty)
1242 pActivityId = &activityId;
1243 if (relActivityId != Guid.Empty)
1244 relatedActivityId = &relActivityId;
1247 #if FEATURE_MANAGED_ETW
1248 if (m_eventData[eventId].EnabledForETW
1249 #if FEATURE_PERFTRACING
1250 || m_eventData[eventId].EnabledForEventPipe
1251 #endif // FEATURE_PERFTRACING
1254 if (!SelfDescribingEvents)
1256 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1257 ThrowEventSourceException(m_eventData[eventId].Name);
1258 #if FEATURE_PERFTRACING
1259 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1260 ThrowEventSourceException(m_eventData[eventId].Name);
1261 #endif // FEATURE_PERFTRACING
1263 else
1265 TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes;
1266 if (tlet == null)
1268 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1269 m_eventData[eventId].Tags,
1270 m_eventData[eventId].Parameters);
1271 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1274 EventSourceOptions opt = new EventSourceOptions
1276 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1277 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1278 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1281 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1284 #endif // FEATURE_MANAGED_ETW
1286 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1287 WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data);
1289 catch (Exception ex)
1291 if (ex is EventSourceException)
1292 throw;
1293 else
1294 ThrowEventSourceException(m_eventData[eventId].Name, ex);
1299 // fallback varags helpers.
1300 /// <summary>
1301 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1302 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1303 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1304 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1305 /// check so that the varargs call is not made when the EventSource is not active.
1306 /// </summary>
1307 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1308 protected unsafe void WriteEvent(int eventId, params object?[] args)
1310 WriteEventVarargs(eventId, null, args);
1313 /// <summary>
1314 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1315 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1316 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1317 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1318 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1319 /// check so that the varargs call is not made when the EventSource is not active.
1320 /// </summary>
1321 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object?[] args)
1323 WriteEventVarargs(eventId, &relatedActivityId, args);
1326 #endregion
1328 #region IDisposable Members
1329 /// <summary>
1330 /// Disposes of an EventSource.
1331 /// </summary>
1332 public void Dispose()
1334 this.Dispose(true);
1335 GC.SuppressFinalize(this);
1337 /// <summary>
1338 /// Disposes of an EventSource.
1339 /// </summary>
1340 /// <remarks>
1341 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1342 /// Guidelines:
1343 /// 1. We may be called more than once: do nothing after the first call.
1344 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1345 /// </remarks>
1346 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1347 protected virtual void Dispose(bool disposing)
1349 if (disposing)
1351 #if FEATURE_MANAGED_ETW
1352 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1353 // even in scenarios with a high volume of ETW events.
1354 if (m_eventSourceEnabled)
1358 SendManifest(m_rawManifest);
1360 catch (Exception)
1361 { } // If it fails, simply give up.
1362 m_eventSourceEnabled = false;
1364 if (m_etwProvider != null)
1366 m_etwProvider.Dispose();
1367 m_etwProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose
1369 #endif
1370 #if FEATURE_PERFTRACING
1371 if (m_eventPipeProvider != null)
1373 m_eventPipeProvider.Dispose();
1374 m_eventPipeProvider = null!; // TODO-NULLABLE: Avoid nulling out in Dispose
1376 #endif
1378 m_eventSourceEnabled = false;
1379 m_eventSourceDisposed = true;
1381 /// <summary>
1382 /// Finalizer for EventSource
1383 /// </summary>
1384 ~EventSource()
1386 this.Dispose(false);
1388 #endregion
1390 #region private
1392 private unsafe void WriteEventRaw(
1393 string? eventName,
1394 ref EventDescriptor eventDescriptor,
1395 IntPtr eventHandle,
1396 Guid* activityID,
1397 Guid* relatedActivityID,
1398 int dataCount,
1399 IntPtr data)
1401 #if FEATURE_MANAGED_ETW
1402 if (m_etwProvider == null)
1404 ThrowEventSourceException(eventName);
1406 else
1408 if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1409 ThrowEventSourceException(eventName);
1411 #endif // FEATURE_MANAGED_ETW
1412 #if FEATURE_PERFTRACING
1413 if (m_eventPipeProvider == null)
1415 ThrowEventSourceException(eventName);
1417 else
1419 if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
1420 ThrowEventSourceException(eventName);
1422 #endif // FEATURE_PERFTRACING
1425 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1426 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1427 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1428 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1431 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1432 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[]? traits = null)
1434 m_config = ValidateSettings(settings);
1435 Initialize(eventSourceGuid, eventSourceName, traits);
1438 /// <summary>
1439 /// This method is responsible for the common initialization path from our constructors. It must
1440 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1441 /// "Log", such an exception would become a cached exception for the initialization of the static
1442 /// member, and any future access to the "Log" would throw the cached exception).
1443 /// </summary>
1444 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1445 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[]? traits)
1449 m_traits = traits;
1450 if (m_traits != null && m_traits.Length % 2 != 0)
1452 throw new ArgumentException(SR.EventSource_TraitEven, nameof(traits));
1455 if (eventSourceGuid == Guid.Empty)
1457 throw new ArgumentException(SR.EventSource_NeedGuid);
1460 if (eventSourceName == null)
1462 throw new ArgumentException(SR.EventSource_NeedName);
1465 m_name = eventSourceName;
1466 m_guid = eventSourceGuid;
1468 //Enable Implicit Activity tracker
1469 m_activityTracker = ActivityTracker.Instance;
1471 #if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
1472 // Create and register our provider traits. We do this early because it is needed to log errors
1473 // In the self-describing event case.
1474 this.InitializeProviderMetadata();
1475 #endif
1476 #if FEATURE_MANAGED_ETW
1477 // Register the provider with ETW
1478 var etwProvider = new OverideEventProvider(this, EventProviderType.ETW);
1479 etwProvider.Register(this);
1480 #endif
1481 #if FEATURE_PERFTRACING
1482 // Register the provider with EventPipe
1483 var eventPipeProvider = new OverideEventProvider(this, EventProviderType.EventPipe);
1484 lock (EventListener.EventListenersLock)
1486 eventPipeProvider.Register(this);
1488 #endif
1489 // Add the eventSource to the global (weak) list.
1490 // This also sets m_id, which is the index in the list.
1491 EventListener.AddEventSource(this);
1493 #if FEATURE_MANAGED_ETW
1494 // OK if we get this far without an exception, then we can at least write out error messages.
1495 // Set m_provider, which allows this.
1496 m_etwProvider = etwProvider;
1498 #if PLATFORM_WINDOWS
1499 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1500 // API available on OS >= Win 8 and patched Win 7.
1501 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1502 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1503 #endif
1505 int setInformationResult;
1506 System.Runtime.InteropServices.GCHandle metadataHandle =
1507 System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1508 IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1510 setInformationResult = m_etwProvider.SetInformation(
1511 Interop.Advapi32.EVENT_INFO_CLASS.SetTraits,
1512 providerMetadata,
1513 (uint)this.providerMetadata.Length);
1515 metadataHandle.Free();
1517 #endif // PLATFORM_WINDOWS
1518 #endif // FEATURE_MANAGED_ETW
1520 #if FEATURE_PERFTRACING
1521 m_eventPipeProvider = eventPipeProvider;
1522 #endif
1523 Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1524 // We are logically completely initialized at this point.
1525 m_completelyInited = true;
1527 catch (Exception e)
1529 if (m_constructionException == null)
1530 m_constructionException = e;
1531 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1534 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1535 lock (EventListener.EventListenersLock)
1537 // If there are any deferred commands, we can do them now.
1538 // This is the most likely place for exceptions to happen.
1539 // Note that we are NOT resetting m_deferredCommands to NULL here,
1540 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1541 EventCommandEventArgs? deferredCommands = m_deferredCommands;
1542 while (deferredCommands != null)
1544 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1545 deferredCommands = deferredCommands.nextCommand;
1550 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1552 if (eventSourceType == null)
1553 throw new ArgumentNullException(nameof(eventSourceType));
1555 EventSourceAttribute? attrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1556 if (attrib != null && attrib.Name != null)
1557 return attrib.Name;
1559 return eventSourceType.Name;
1562 /// <summary>
1563 /// Implements the SHA1 hashing algorithm. Note that this
1564 /// implementation is for hashing public information. Do not
1565 /// use this code to hash private data, as this implementation does
1566 /// not take any steps to avoid information disclosure.
1567 /// </summary>
1568 private struct Sha1ForNonSecretPurposes
1570 private long length; // Total message length in bits
1571 private uint[] w; // Workspace
1572 private int pos; // Length of current chunk in bytes
1574 /// <summary>
1575 /// Call Start() to initialize the hash object.
1576 /// </summary>
1577 public void Start()
1579 if (this.w == null)
1581 this.w = new uint[85];
1584 this.length = 0;
1585 this.pos = 0;
1586 this.w[80] = 0x67452301;
1587 this.w[81] = 0xEFCDAB89;
1588 this.w[82] = 0x98BADCFE;
1589 this.w[83] = 0x10325476;
1590 this.w[84] = 0xC3D2E1F0;
1593 /// <summary>
1594 /// Adds an input byte to the hash.
1595 /// </summary>
1596 /// <param name="input">Data to include in the hash.</param>
1597 public void Append(byte input)
1599 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1600 if (64 == ++this.pos)
1602 this.Drain();
1606 /// <summary>
1607 /// Adds input bytes to the hash.
1608 /// </summary>
1609 /// <param name="input">
1610 /// Data to include in the hash. Must not be null.
1611 /// </param>
1612 public void Append(byte[] input)
1614 foreach (byte b in input)
1616 this.Append(b);
1620 /// <summary>
1621 /// Retrieves the hash value.
1622 /// Note that after calling this function, the hash object should
1623 /// be considered uninitialized. Subsequent calls to Append or
1624 /// Finish will produce useless results. Call Start() to
1625 /// reinitialize.
1626 /// </summary>
1627 /// <param name="output">
1628 /// Buffer to receive the hash value. Must not be null.
1629 /// Up to 20 bytes of hash will be written to the output buffer.
1630 /// If the buffer is smaller than 20 bytes, the remaining hash
1631 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1632 /// rest of the buffer is left unmodified.
1633 /// </param>
1634 public void Finish(byte[] output)
1636 long l = this.length + 8 * this.pos;
1637 this.Append(0x80);
1638 while (this.pos != 56)
1640 this.Append(0x00);
1643 unchecked
1645 this.Append((byte)(l >> 56));
1646 this.Append((byte)(l >> 48));
1647 this.Append((byte)(l >> 40));
1648 this.Append((byte)(l >> 32));
1649 this.Append((byte)(l >> 24));
1650 this.Append((byte)(l >> 16));
1651 this.Append((byte)(l >> 8));
1652 this.Append((byte)l);
1654 int end = output.Length < 20 ? output.Length : 20;
1655 for (int i = 0; i != end; i++)
1657 uint temp = this.w[80 + i / 4];
1658 output[i] = (byte)(temp >> 24);
1659 this.w[80 + i / 4] = temp << 8;
1664 /// <summary>
1665 /// Called when this.pos reaches 64.
1666 /// </summary>
1667 private void Drain()
1669 for (int i = 16; i != 80; i++)
1671 this.w[i] = BitOperations.RotateLeft((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]), 1);
1674 unchecked
1676 uint a = this.w[80];
1677 uint b = this.w[81];
1678 uint c = this.w[82];
1679 uint d = this.w[83];
1680 uint e = this.w[84];
1682 for (int i = 0; i != 20; i++)
1684 const uint k = 0x5A827999;
1685 uint f = (b & c) | ((~b) & d);
1686 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;
1689 for (int i = 20; i != 40; i++)
1691 uint f = b ^ c ^ d;
1692 const uint k = 0x6ED9EBA1;
1693 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;
1696 for (int i = 40; i != 60; i++)
1698 uint f = (b & c) | (b & d) | (c & d);
1699 const uint k = 0x8F1BBCDC;
1700 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;
1703 for (int i = 60; i != 80; i++)
1705 uint f = b ^ c ^ d;
1706 const uint k = 0xCA62C1D6;
1707 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;
1710 this.w[80] += a;
1711 this.w[81] += b;
1712 this.w[82] += c;
1713 this.w[83] += d;
1714 this.w[84] += e;
1717 this.length += 512; // 64 bytes == 512 bits
1718 this.pos = 0;
1722 private static Guid GenerateGuidFromName(string name)
1724 if (namespaceBytes == null)
1726 namespaceBytes = new byte[] {
1727 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
1728 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
1732 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1733 var hash = new Sha1ForNonSecretPurposes();
1734 hash.Start();
1735 hash.Append(namespaceBytes);
1736 hash.Append(bytes);
1737 Array.Resize(ref bytes, 16);
1738 hash.Finish(bytes);
1740 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1741 return new Guid(bytes);
1744 private unsafe object? DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1746 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1747 // the recursion, but can we do this in a robust way?
1749 IntPtr dataPointer = data->DataPointer;
1750 // advance to next EventData in array
1751 ++data;
1753 Debug.Assert(m_eventData != null);
1754 Type dataType = GetDataType(m_eventData[eventId], parameterId);
1756 Again:
1757 if (dataType == typeof(IntPtr))
1759 return *((IntPtr*)dataPointer);
1761 else if (dataType == typeof(int))
1763 return *((int*)dataPointer);
1765 else if (dataType == typeof(uint))
1767 return *((uint*)dataPointer);
1769 else if (dataType == typeof(long))
1771 return *((long*)dataPointer);
1773 else if (dataType == typeof(ulong))
1775 return *((ulong*)dataPointer);
1777 else if (dataType == typeof(byte))
1779 return *((byte*)dataPointer);
1781 else if (dataType == typeof(sbyte))
1783 return *((sbyte*)dataPointer);
1785 else if (dataType == typeof(short))
1787 return *((short*)dataPointer);
1789 else if (dataType == typeof(ushort))
1791 return *((ushort*)dataPointer);
1793 else if (dataType == typeof(float))
1795 return *((float*)dataPointer);
1797 else if (dataType == typeof(double))
1799 return *((double*)dataPointer);
1801 else if (dataType == typeof(decimal))
1803 return *((decimal*)dataPointer);
1805 else if (dataType == typeof(bool))
1807 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1808 if (*((int*)dataPointer) == 1)
1810 return true;
1812 else
1814 return false;
1817 else if (dataType == typeof(Guid))
1819 return *((Guid*)dataPointer);
1821 else if (dataType == typeof(char))
1823 return *((char*)dataPointer);
1825 else if (dataType == typeof(DateTime))
1827 long dateTimeTicks = *((long*)dataPointer);
1828 return DateTime.FromFileTimeUtc(dateTimeTicks);
1830 else if (dataType == typeof(byte[]))
1832 // byte[] are written to EventData* as an int followed by a blob
1833 int cbSize = *((int*)dataPointer);
1834 byte[] blob = new byte[cbSize];
1835 dataPointer = data->DataPointer;
1836 data++;
1837 for (int i = 0; i < cbSize; ++i)
1838 blob[i] = *((byte*)(dataPointer + i));
1839 return blob;
1841 else if (dataType == typeof(byte*))
1843 // TODO: how do we want to handle this? For now we ignore it...
1844 return null;
1846 else
1848 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1850 return null;
1855 m_EventSourceInDecodeObject = true;
1857 if (dataType.IsEnum())
1859 dataType = Enum.GetUnderlyingType(dataType);
1860 #if ES_BUILD_PN
1861 int dataTypeSize = (int)dataType.TypeHandle.ToEETypePtr().ValueTypeSize;
1862 #else
1863 int dataTypeSize = System.Runtime.InteropServices.Marshal.SizeOf(dataType);
1864 #endif
1865 if (dataTypeSize < sizeof(int))
1866 dataType = typeof(int);
1867 goto Again;
1870 // Everything else is marshaled as a string.
1871 // ETW strings are NULL-terminated, so marshal everything up to the first
1872 // null in the string.
1873 //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1874 if (dataPointer == IntPtr.Zero)
1876 return null;
1879 return new string((char *)dataPointer);
1881 finally
1883 m_EventSourceInDecodeObject = false;
1888 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1889 // eventSource).
1890 private EventDispatcher? GetDispatcher(EventListener? listener)
1892 EventDispatcher? dispatcher = m_Dispatchers;
1893 while (dispatcher != null)
1895 if (dispatcher.m_Listener == listener)
1896 return dispatcher;
1897 dispatcher = dispatcher.m_Next;
1899 return dispatcher;
1902 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object?[] args)
1904 if (m_eventSourceEnabled)
1906 Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1909 if (childActivityID != null)
1911 ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1913 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1914 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1915 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1916 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1917 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1918 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1919 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1920 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1921 if (!m_eventData[eventId].HasRelatedActivityID)
1923 throw new ArgumentException(SR.EventSource_NoRelatedActivityId);
1927 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1929 Guid* pActivityId = null;
1930 Guid activityId = Guid.Empty;
1931 Guid relatedActivityId = Guid.Empty;
1932 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1933 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1935 if (childActivityID == null &&
1936 ((activityOptions & EventActivityOptions.Disable) == 0))
1938 if (opcode == EventOpcode.Start)
1940 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1942 else if (opcode == EventOpcode.Stop)
1944 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1947 if (activityId != Guid.Empty)
1948 pActivityId = &activityId;
1949 if (relatedActivityId != Guid.Empty)
1950 childActivityID = &relatedActivityId;
1953 #if FEATURE_MANAGED_ETW
1954 if (m_eventData[eventId].EnabledForETW
1955 #if FEATURE_PERFTRACING
1956 || m_eventData[eventId].EnabledForEventPipe
1957 #endif // FEATURE_PERFTRACING
1960 if (!SelfDescribingEvents)
1962 if (!m_etwProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1963 ThrowEventSourceException(m_eventData[eventId].Name);
1964 #if FEATURE_PERFTRACING
1965 if (!m_eventPipeProvider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
1966 ThrowEventSourceException(m_eventData[eventId].Name);
1967 #endif // FEATURE_PERFTRACING
1969 else
1971 TraceLoggingEventTypes? tlet = m_eventData[eventId].TraceLoggingEventTypes;
1972 if (tlet == null)
1974 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1975 EventTags.None,
1976 m_eventData[eventId].Parameters);
1977 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1980 // TODO: activity ID support
1981 EventSourceOptions opt = new EventSourceOptions
1983 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1984 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1985 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1988 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
1991 #endif // FEATURE_MANAGED_ETW
1992 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1994 #if !ES_BUILD_STANDALONE
1995 // Maintain old behavior - object identity is preserved
1996 if (LocalAppContextSwitches.PreserveEventListnerObjectIdentity)
1998 WriteToAllListeners(
1999 eventId: eventId,
2000 osThreadId: null,
2001 timeStamp: null,
2002 activityID: pActivityId,
2003 childActivityID: childActivityID,
2004 args: args);
2006 else
2007 #endif // !ES_BUILD_STANDALONE
2009 object?[] serializedArgs = SerializeEventArgs(eventId, args);
2010 WriteToAllListeners(
2011 eventId: eventId,
2012 osThreadId: null,
2013 timeStamp: null,
2014 activityID: pActivityId,
2015 childActivityID: childActivityID,
2016 args: serializedArgs);
2020 catch (Exception ex)
2022 if (ex is EventSourceException)
2023 throw;
2024 else
2025 ThrowEventSourceException(m_eventData[eventId].Name, ex);
2030 private unsafe object?[] SerializeEventArgs(int eventId, object?[] args)
2032 Debug.Assert(m_eventData != null);
2033 TraceLoggingEventTypes? eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2034 if (eventTypes == null)
2036 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2037 EventTags.None,
2038 m_eventData[eventId].Parameters);
2039 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2041 var eventData = new object?[eventTypes.typeInfos.Length];
2042 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2044 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2046 return eventData;
2049 /// <summary>
2050 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2051 /// checks that they in fact match and logs a warning to the debugger if they don't.
2052 /// </summary>
2053 /// <param name="infos"></param>
2054 /// <param name="args"></param>
2055 private void LogEventArgsMismatches(ParameterInfo[] infos, object?[] args)
2057 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2058 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2059 // writing to the debugger log on PCL.
2060 bool typesMatch = args.Length == infos.Length;
2062 int i = 0;
2063 while (typesMatch && i < args.Length)
2065 Type pType = infos[i].ParameterType;
2067 Type? argType = args[i]?.GetType();
2069 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2070 // Fail if one of two things hold : either the argument type is not equal or assignable to the parameter type, or the
2071 // argument is null and the parameter type is a non-Nullable<T> value type.
2072 if ((args[i] != null && !pType.IsAssignableFrom(argType))
2073 || (args[i] == null && !((pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>)) || !pType.IsValueType)))
2075 typesMatch = false;
2076 break;
2079 ++i;
2082 if (!typesMatch)
2084 System.Diagnostics.Debugger.Log(0, null, SR.EventSource_VarArgsParameterMismatch + "\r\n");
2086 #endif //!ES_BUILD_PCL
2089 private unsafe void WriteToAllListeners(int eventId, Guid* activityID, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2091 Debug.Assert(m_eventData != null);
2092 // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
2093 // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
2094 // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
2095 // we just used the modifiedParamCount here -- so we need both.
2096 int paramCount = GetParameterCount(m_eventData[eventId]);
2097 int modifiedParamCount = 0;
2098 for (int i = 0; i < paramCount; i++)
2100 Type parameterType = GetDataType(m_eventData[eventId], i);
2101 if (parameterType == typeof(byte[]))
2103 modifiedParamCount += 2;
2105 else
2107 modifiedParamCount++;
2110 if (eventDataCount != modifiedParamCount)
2112 ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, eventDataCount, paramCount), true);
2113 paramCount = Math.Min(paramCount, eventDataCount);
2116 object?[] args = new object[paramCount];
2118 EventSource.EventData* dataPtr = data;
2119 for (int i = 0; i < paramCount; i++)
2120 args[i] = DecodeObject(eventId, i, ref dataPtr);
2121 WriteToAllListeners(
2122 eventId: eventId,
2123 osThreadId: null,
2124 timeStamp: null,
2125 activityID: activityID,
2126 childActivityID: childActivityID,
2127 args: args);
2130 // helper for writing to all EventListeners attached the current eventSource.
2131 internal unsafe void WriteToAllListeners(int eventId, uint* osThreadId, DateTime* timeStamp, Guid* activityID, Guid* childActivityID, params object?[] args)
2133 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2134 eventCallbackArgs.EventId = eventId;
2135 if (osThreadId != null)
2136 eventCallbackArgs.OSThreadId = (int)*osThreadId;
2137 if (timeStamp != null)
2138 eventCallbackArgs.TimeStamp = *timeStamp;
2139 if (activityID != null)
2140 eventCallbackArgs.ActivityId = *activityID;
2141 if (childActivityID != null)
2142 eventCallbackArgs.RelatedActivityId = *childActivityID;
2144 Debug.Assert(m_eventData != null);
2145 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2146 eventCallbackArgs.Message = m_eventData[eventId].Message;
2147 eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(args);
2149 DispatchToAllListeners(eventId, eventCallbackArgs);
2152 private unsafe void DispatchToAllListeners(int eventId, EventWrittenEventArgs eventCallbackArgs)
2154 Exception? lastThrownException = null;
2155 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2157 Debug.Assert(dispatcher.m_EventEnabled != null);
2158 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2163 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2165 catch (Exception e)
2167 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2168 + e.Message, false);
2169 lastThrownException = e;
2175 if (lastThrownException != null)
2177 throw new EventSourceException(lastThrownException);
2181 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2182 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2184 #if FEATURE_MANAGED_ETW
2185 if (m_etwProvider != null)
2187 string eventName = "EventSourceMessage";
2188 if (SelfDescribingEvents)
2190 EventSourceOptions opt = new EventSourceOptions
2192 Keywords = (EventKeywords)unchecked(keywords),
2193 Level = level
2195 var msg = new { message = msgString };
2196 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2197 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2199 else
2201 // We want the name of the provider to show up so if we don't have a manifest we create
2202 // on that at least has the provider name (I don't define any events).
2203 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2205 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2206 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2207 manifestBuilder.AddEventParameter(typeof(string), "message");
2208 manifestBuilder.EndEvent();
2209 SendManifest(manifestBuilder.CreateManifest());
2212 // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited.
2213 fixed (char* msgStringPtr = msgString)
2215 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2216 EventProvider.EventData data = new EventProvider.EventData();
2217 data.Ptr = (ulong)msgStringPtr;
2218 data.Size = (uint)(2 * (msgString.Length + 1));
2219 data.Reserved = 0;
2220 m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
2224 #endif // FEATURE_MANAGED_ETW
2227 /// <summary>
2228 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2229 /// while writing the message to any one of the listeners will be silently ignored.
2230 /// </summary>
2231 private void WriteStringToAllListeners(string eventName, string msg)
2233 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2234 eventCallbackArgs.EventId = 0;
2235 eventCallbackArgs.Message = msg;
2236 eventCallbackArgs.Payload = new ReadOnlyCollection<object?>(new List<object?>() { msg });
2237 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2238 eventCallbackArgs.EventName = eventName;
2240 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2242 bool dispatcherEnabled = false;
2243 if (dispatcher.m_EventEnabled == null)
2245 // if the listeners that weren't correctly initialized, we will send to it
2246 // since this is an error message and we want to see it go out.
2247 dispatcherEnabled = true;
2249 else
2251 // if there's *any* enabled event on the dispatcher we'll write out the string
2252 // otherwise we'll treat the listener as disabled and skip it
2253 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2255 if (dispatcher.m_EventEnabled[evtId])
2257 dispatcherEnabled = true;
2258 break;
2264 if (dispatcherEnabled)
2265 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2267 catch
2269 // ignore any exceptions thrown by listeners' OnEventWritten
2274 /// <summary>
2275 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2276 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2277 /// </summary>
2278 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2280 if (!enable)
2281 return false;
2283 Debug.Assert(m_eventData != null);
2284 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2285 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2287 #if FEATURE_MANAGED_ETW_CHANNELS
2288 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2289 #else
2290 EventChannel channel = EventChannel.None;
2291 #endif
2293 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2296 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2297 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2299 if (!enabled)
2300 return false;
2302 // does is pass the level test?
2303 if ((currentLevel != 0) && (currentLevel < eventLevel))
2304 return false;
2306 // if yes, does it pass the keywords test?
2307 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2309 #if FEATURE_MANAGED_ETW_CHANNELS
2310 // is there a channel with keywords that match currentMatchAnyKeyword?
2311 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2313 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2314 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2315 return false;
2317 else
2318 #endif
2320 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2321 return false;
2324 return true;
2327 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2328 private void ThrowEventSourceException(string? eventName, Exception? innerEx = null)
2330 // If we fail during out of band logging we may end up trying
2331 // to throw another EventSourceException, thus hitting a StackOverflowException.
2332 // Avoid StackOverflow by making sure we do not recursively call this method.
2333 if (m_EventSourceExceptionRecurenceCount > 0)
2334 return;
2337 m_EventSourceExceptionRecurenceCount++;
2339 string errorPrefix = "EventSourceException";
2340 if (eventName != null)
2342 errorPrefix += " while processing event \"" + eventName + "\"";
2345 // TODO Create variations of EventSourceException that indicate more information using the error code.
2346 switch (EventProvider.GetLastWriteEventError())
2348 case EventProvider.WriteEventErrorCode.EventTooBig:
2349 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_EventTooBig, true);
2350 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_EventTooBig, innerEx);
2351 break;
2352 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2353 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NoFreeBuffers, true);
2354 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NoFreeBuffers, innerEx);
2355 break;
2356 case EventProvider.WriteEventErrorCode.NullInput:
2357 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NullInput, true);
2358 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NullInput, innerEx);
2359 break;
2360 case EventProvider.WriteEventErrorCode.TooManyArgs:
2361 ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_TooManyArgs, true);
2362 if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_TooManyArgs, innerEx);
2363 break;
2364 default:
2365 if (innerEx != null)
2367 innerEx = innerEx.GetBaseException();
2368 ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2370 else
2371 ReportOutOfBandMessage(errorPrefix, true);
2372 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2373 break;
2376 finally
2378 m_EventSourceExceptionRecurenceCount--;
2382 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string? eventName)
2384 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2385 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2386 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2388 ThrowEventSourceException(eventName);
2392 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eventName)
2394 if (opcode == EventOpcode.Info && eventName != null)
2396 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2398 return EventOpcode.Start;
2400 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2402 return EventOpcode.Stop;
2406 return opcode;
2409 #if FEATURE_MANAGED_ETW
2410 /// <summary>
2411 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2412 /// </summary>
2413 private class OverideEventProvider : EventProvider
2415 public OverideEventProvider(EventSource eventSource, EventProviderType providerType)
2416 : base(providerType)
2418 this.m_eventSource = eventSource;
2419 this.m_eventProviderType = providerType;
2421 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string?>? arguments,
2422 int perEventSourceSessionId, int etwSessionId)
2424 // We use null to represent the ETW EventListener.
2425 EventListener? listener = null;
2426 m_eventSource.SendCommand(listener, m_eventProviderType, perEventSourceSessionId, etwSessionId,
2427 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2429 private readonly EventSource m_eventSource;
2430 private readonly EventProviderType m_eventProviderType;
2432 #endif
2434 /// <summary>
2435 /// Used to hold all the static information about an event. This includes everything in the event
2436 /// descriptor as well as some stuff we added specifically for EventSource. see the
2437 /// code:m_eventData for where we use this.
2438 /// </summary>
2441 EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2442 now the move to CoreLib marked them as private.
2443 While they are technically private (it's a contract used between the library and the ILC toolchain),
2444 we need them to be rooted and exported from shared library for the system to work.
2445 For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2446 root them and modify shared library definition to force export them.
2448 #if ES_BUILD_PN
2449 public
2450 #else
2451 internal
2452 #endif
2453 partial struct EventMetadata
2455 #if ES_BUILD_PN
2456 public EventMetadata(EventDescriptor descriptor,
2457 EventTags tags,
2458 bool enabledForAnyListener,
2459 bool enabledForETW,
2460 string name,
2461 string message,
2462 EventParameterType[] parameterTypes)
2464 this.Descriptor = descriptor;
2465 this.Tags = tags;
2466 this.EnabledForAnyListener = enabledForAnyListener;
2467 this.EnabledForETW = enabledForETW;
2468 #if FEATURE_PERFTRACING
2469 this.EnabledForEventPipe = false;
2470 #endif
2471 this.TriggersActivityTracking = 0;
2472 this.Name = name;
2473 this.Message = message;
2474 this.Parameters = null!;
2475 this.TraceLoggingEventTypes = null;
2476 this.ActivityOptions = EventActivityOptions.None;
2477 this.ParameterTypes = parameterTypes;
2478 this.HasRelatedActivityID = false;
2479 this.EventHandle = IntPtr.Zero;
2481 #endif
2483 public EventDescriptor Descriptor;
2484 public IntPtr EventHandle; // EventPipeEvent handle.
2485 public EventTags Tags;
2486 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2487 public bool EnabledForETW; // is this event on for ETW?
2488 #if FEATURE_PERFTRACING
2489 public bool EnabledForEventPipe; // is this event on for EventPipe?
2490 #endif
2492 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2493 #pragma warning disable 0649
2494 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2495 #pragma warning restore 0649
2496 public string Name; // the name of the event
2497 public string? Message; // If the event has a message associated with it, this is it.
2498 public ParameterInfo[] Parameters; // TODO can we remove?
2500 public TraceLoggingEventTypes? TraceLoggingEventTypes;
2501 public EventActivityOptions ActivityOptions;
2503 #if ES_BUILD_PN
2504 public EventParameterType[] ParameterTypes;
2505 #endif
2508 #if !ES_BUILD_PN
2509 private int GetParameterCount(EventMetadata eventData)
2511 return eventData.Parameters.Length;
2514 private Type GetDataType(EventMetadata eventData, int parameterId)
2516 return eventData.Parameters[parameterId].ParameterType;
2519 private const bool m_EventSourcePreventRecursion = false;
2520 #else
2521 private int GetParameterCount(EventMetadata eventData)
2523 int paramCount;
2524 if (eventData.Parameters == null)
2526 paramCount = eventData.ParameterTypes.Length;
2528 else
2530 paramCount = eventData.Parameters.Length;
2533 return paramCount;
2536 private Type GetDataType(EventMetadata eventData, int parameterId)
2538 Type dataType;
2539 if (eventData.Parameters == null)
2541 dataType = EventTypeToType(eventData.ParameterTypes[parameterId]);
2543 else
2545 dataType = eventData.Parameters[parameterId].ParameterType;
2548 return dataType;
2551 private static readonly bool m_EventSourcePreventRecursion = true;
2553 public enum EventParameterType
2555 Boolean,
2556 Byte,
2557 SByte,
2558 Char,
2559 Int16,
2560 UInt16,
2561 Int32,
2562 UInt32,
2563 Int64,
2564 UInt64,
2565 IntPtr,
2566 Single,
2567 Double,
2568 Decimal,
2569 Guid,
2570 String
2573 private Type EventTypeToType(EventParameterType type)
2575 switch (type)
2577 case EventParameterType.Boolean:
2578 return typeof(bool);
2579 case EventParameterType.Byte:
2580 return typeof(byte);
2581 case EventParameterType.SByte:
2582 return typeof(sbyte);
2583 case EventParameterType.Char:
2584 return typeof(char);
2585 case EventParameterType.Int16:
2586 return typeof(short);
2587 case EventParameterType.UInt16:
2588 return typeof(ushort);
2589 case EventParameterType.Int32:
2590 return typeof(int);
2591 case EventParameterType.UInt32:
2592 return typeof(uint);
2593 case EventParameterType.Int64:
2594 return typeof(long);
2595 case EventParameterType.UInt64:
2596 return typeof(ulong);
2597 case EventParameterType.IntPtr:
2598 return typeof(IntPtr);
2599 case EventParameterType.Single:
2600 return typeof(float);
2601 case EventParameterType.Double:
2602 return typeof(double);
2603 case EventParameterType.Decimal:
2604 return typeof(decimal);
2605 case EventParameterType.Guid:
2606 return typeof(Guid);
2607 case EventParameterType.String:
2608 return typeof(string);
2609 default:
2610 // TODO: should I throw an exception here?
2611 return null!;
2614 #endif
2616 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2617 // eventSource. The logic is as follows
2619 // * if Command == Update
2620 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2621 // to (if listener != null)
2622 // perEventSourceSessionId = 0 - reserved for EventListeners
2623 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2624 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2625 // Keywords that identifies the session
2626 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2627 // discriminated by etwSessionId
2628 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2629 // activity tracing across different providers (which might have different sessionIds
2630 // for the same ETW session)
2631 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2632 // eventSource. In particular, if 'enabled' is false, 'level' and
2633 // 'matchAnyKeywords' are not used.
2634 // * OnEventCommand is invoked, which may cause calls to
2635 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2636 // depending on the logic in that routine.
2637 // * else (command != Update)
2638 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2639 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2641 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2642 internal void SendCommand(EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId,
2643 EventCommand command, bool enable,
2644 EventLevel level, EventKeywords matchAnyKeyword,
2645 IDictionary<string, string?>? commandArguments)
2647 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, eventProviderType, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2648 lock (EventListener.EventListenersLock)
2650 if (m_completelyInited)
2652 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2653 this.m_deferredCommands = null;
2654 // We are fully initialized, do the command
2655 DoCommand(commandArgs);
2657 else
2659 // We can't do the command, simply remember it and we do it when we are fully constructed.
2660 if (m_deferredCommands == null)
2661 m_deferredCommands = commandArgs; // create the first entry
2662 else
2664 // We have one or more entries, find the last one and add it to that.
2665 EventCommandEventArgs lastCommand = m_deferredCommands;
2666 while (lastCommand.nextCommand != null)
2667 lastCommand = lastCommand.nextCommand;
2668 lastCommand.nextCommand = commandArgs;
2674 /// <summary>
2675 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2676 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2677 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2678 /// This helper actually does all actual command logic.
2679 /// </summary>
2680 internal void DoCommand(EventCommandEventArgs commandArgs)
2682 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2683 // We defer commands until we are completely inited. This allows error messages to be sent.
2684 Debug.Assert(m_completelyInited);
2686 #if FEATURE_MANAGED_ETW
2687 if (m_etwProvider == null) // If we failed to construct
2688 return;
2689 #endif // FEATURE_MANAGED_ETW
2690 #if FEATURE_PERFTRACING
2691 if (m_eventPipeProvider == null)
2692 return;
2693 #endif
2695 m_outOfBandMessageCount = 0;
2696 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2699 EnsureDescriptorsInitialized();
2700 Debug.Assert(m_eventData != null);
2702 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2703 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2704 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2706 throw new ArgumentException(SR.EventSource_ListenerNotFound);
2709 if (commandArgs.Arguments == null)
2710 commandArgs.Arguments = new Dictionary<string, string?>();
2712 if (commandArgs.Command == EventCommand.Update)
2714 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2715 for (int i = 0; i < m_eventData.Length; i++)
2716 EnableEventForDispatcher(commandArgs.dispatcher, commandArgs.eventProviderType, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2718 if (commandArgs.enable)
2720 if (!m_eventSourceEnabled)
2722 // EventSource turned on for the first time, simply copy the bits.
2723 m_level = commandArgs.level;
2724 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2726 else
2728 // Already enabled, make it the most verbose of the existing and new filter
2729 if (commandArgs.level > m_level)
2730 m_level = commandArgs.level;
2731 if (commandArgs.matchAnyKeyword == 0)
2732 m_matchAnyKeyword = 0;
2733 else if (m_matchAnyKeyword != 0)
2734 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2738 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2739 // represent 0-based positive values
2740 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2741 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2742 bSessionEnable = false;
2744 if (commandArgs.listener == null)
2746 if (!bSessionEnable)
2747 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2748 // for "global" enable/disable (passed in with listener == null and
2749 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2750 --commandArgs.perEventSourceSessionId;
2753 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2755 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2756 // hasn't changed.
2757 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2758 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2759 Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2761 // Send the manifest if we are enabling an ETW session
2762 if (bSessionEnable && commandArgs.dispatcher == null)
2764 // eventSourceDispatcher == null means this is the ETW manifest
2766 // Note that we unconditionally send the manifest whenever we are enabled, even if
2767 // we were already enabled. This is because there may be multiple sessions active
2768 // and we can't know that all the sessions have seen the manifest.
2769 if (!SelfDescribingEvents)
2770 SendManifest(m_rawManifest);
2773 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2774 // things like log messages, or test if keywords are enabled in the callback.
2775 if (commandArgs.enable)
2777 Debug.Assert(m_eventData != null);
2778 m_eventSourceEnabled = true;
2781 this.OnEventCommand(commandArgs);
2782 this.m_eventCommandExecuted?.Invoke(this, commandArgs);
2784 if (!commandArgs.enable)
2786 // If we are disabling, maybe we can turn on 'quick checks' to filter
2787 // quickly. These are all just optimizations (since later checks will still filter)
2789 // There is a good chance EnabledForAnyListener are not as accurate as
2790 // they could be, go ahead and get a better estimate.
2791 for (int i = 0; i < m_eventData.Length; i++)
2793 bool isEnabledForAnyListener = false;
2794 for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2796 Debug.Assert(dispatcher.m_EventEnabled != null);
2798 if (dispatcher.m_EventEnabled[i])
2800 isEnabledForAnyListener = true;
2801 break;
2804 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2807 // If no events are enabled, disable the global enabled bit.
2808 if (!AnyEventEnabled())
2810 m_level = 0;
2811 m_matchAnyKeyword = 0;
2812 m_eventSourceEnabled = false;
2816 else
2818 #if !FEATURE_PERFTRACING
2819 if (commandArgs.Command == EventCommand.SendManifest)
2821 // TODO: should we generate the manifest here if we hadn't already?
2822 if (m_rawManifest != null)
2823 SendManifest(m_rawManifest);
2825 #endif
2827 // These are not used for non-update commands and thus should always be 'default' values
2828 // Debug.Assert(enable == true);
2829 // Debug.Assert(level == EventLevel.LogAlways);
2830 // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2832 this.OnEventCommand(commandArgs);
2833 m_eventCommandExecuted?.Invoke(this, commandArgs);
2836 catch (Exception e)
2838 // When the ETW session is created after the EventSource has registered with the ETW system
2839 // we can send any error messages here.
2840 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2841 // We never throw when doing a command.
2845 /// <summary>
2846 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2847 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2848 /// range return false, otherwise true.
2849 /// </summary>
2850 internal bool EnableEventForDispatcher(EventDispatcher? dispatcher, EventProviderType eventProviderType, int eventId, bool value)
2852 Debug.Assert(m_eventData != null);
2854 if (dispatcher == null)
2856 if (eventId >= m_eventData.Length)
2857 return false;
2858 #if FEATURE_MANAGED_ETW
2859 if (m_etwProvider != null && eventProviderType == EventProviderType.ETW)
2860 m_eventData[eventId].EnabledForETW = value;
2861 #endif
2862 #if FEATURE_PERFTRACING
2863 if (m_eventPipeProvider != null && eventProviderType == EventProviderType.EventPipe)
2864 m_eventData[eventId].EnabledForEventPipe = value;
2865 #endif
2867 else
2869 Debug.Assert(dispatcher.m_EventEnabled != null);
2870 if (eventId >= dispatcher.m_EventEnabled.Length)
2871 return false;
2872 dispatcher.m_EventEnabled[eventId] = value;
2873 if (value)
2874 m_eventData[eventId].EnabledForAnyListener = true;
2876 return true;
2879 /// <summary>
2880 /// Returns true if any event at all is on.
2881 /// </summary>
2882 private bool AnyEventEnabled()
2884 Debug.Assert(m_eventData != null);
2886 for (int i = 0; i < m_eventData.Length; i++)
2887 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener
2888 #if FEATURE_PERFTRACING
2889 || m_eventData[i].EnabledForEventPipe
2890 #endif // FEATURE_PERFTRACING
2892 return true;
2893 return false;
2896 private bool IsDisposed => m_eventSourceDisposed;
2898 private void EnsureDescriptorsInitialized()
2900 #if !ES_BUILD_STANDALONE
2901 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
2902 #endif
2903 if (m_eventData == null)
2905 Guid eventSourceGuid = Guid.Empty;
2906 string? eventSourceName = null;
2907 EventMetadata[]? eventData = null;
2908 byte[]? manifest = null;
2910 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
2911 // to the reflection approach.
2912 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
2914 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
2916 // GetMetadata failed, so we have to set it via reflection.
2917 Debug.Assert(m_rawManifest == null);
2919 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
2920 Debug.Assert(m_eventData != null);
2922 else
2924 // GetMetadata worked, so set the fields as appropriate.
2925 m_name = eventSourceName;
2926 m_guid = eventSourceGuid;
2927 m_eventData = eventData;
2928 m_rawManifest = manifest;
2930 // TODO Enforce singleton pattern
2931 Debug.Assert(EventListener.s_EventSources != null, "should be called within lock on EventListener.EventListenersLock which ensures s_EventSources to be initialized");
2932 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
2934 if (eventSourceRef.Target is EventSource eventSource && eventSource.Guid == m_guid && !eventSource.IsDisposed)
2936 if (eventSource != this)
2938 throw new ArgumentException(SR.Format(SR.EventSource_EventSourceGuidInUse, m_guid));
2943 // Make certain all dispatchers also have their arrays initialized
2944 EventDispatcher? dispatcher = m_Dispatchers;
2945 while (dispatcher != null)
2947 if (dispatcher.m_EventEnabled == null)
2948 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
2949 dispatcher = dispatcher.m_Next;
2951 #if FEATURE_PERFTRACING
2952 // Initialize the EventPipe event handles.
2953 DefineEventPipeEvents();
2954 #endif
2956 if (s_currentPid == 0)
2958 #if ES_BUILD_STANDALONE
2959 // for non-BCL EventSource we must assert SecurityPermission
2960 new SecurityPermission(PermissionState.Unrestricted).Assert();
2961 #endif
2962 s_currentPid = Interop.GetCurrentProcessId();
2966 // Send out the ETW manifest XML out to ETW
2967 // Today, we only send the manifest to ETW, custom listeners don't get it.
2968 private unsafe bool SendManifest(byte[]? rawManifest)
2970 bool success = true;
2972 if (rawManifest == null)
2973 return false;
2975 Debug.Assert(!SelfDescribingEvents);
2977 #if FEATURE_MANAGED_ETW
2978 fixed (byte* dataPtr = rawManifest)
2980 // we don't want the manifest to show up in the event log channels so we specify as keywords
2981 // everything but the first 8 bits (reserved for the 8 channels)
2982 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
2983 ManifestEnvelope envelope = new ManifestEnvelope();
2985 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
2986 envelope.MajorVersion = 1;
2987 envelope.MinorVersion = 0;
2988 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
2989 int dataLeft = rawManifest.Length;
2990 envelope.ChunkNumber = 0;
2992 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
2994 dataDescrs[0].Ptr = (ulong)&envelope;
2995 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
2996 dataDescrs[0].Reserved = 0;
2998 dataDescrs[1].Ptr = (ulong)dataPtr;
2999 dataDescrs[1].Reserved = 0;
3001 int chunkSize = ManifestEnvelope.MaxChunkSize;
3002 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3003 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3004 while (dataLeft > 0)
3006 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3007 if (m_etwProvider != null)
3009 if (!m_etwProvider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
3011 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3012 // can fail. If we get this failure on the first chunk try again with something smaller
3013 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3014 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3016 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3018 chunkSize = chunkSize / 2;
3019 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3022 success = false;
3023 if (ThrowOnEventWriteErrors)
3024 ThrowEventSourceException("SendManifest");
3025 break;
3028 dataLeft -= chunkSize;
3029 dataDescrs[1].Ptr += (uint)chunkSize;
3030 envelope.ChunkNumber++;
3032 // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
3033 // 5 chunks, so only the largest manifests will hit the pause.
3034 if ((envelope.ChunkNumber % 5) == 0)
3036 Thread.Sleep(15);
3040 #endif // FEATURE_MANAGED_ETW
3041 return success;
3044 #if (ES_BUILD_PCL)
3045 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3047 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3049 #endif
3051 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3052 // When that is the case, we have the build the custom assemblies on a member by hand.
3053 internal static Attribute? GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3055 #if !ES_BUILD_PN
3056 // On ProjectN, ReflectionOnly() always equals false. AllowEventSourceOverride is an option that allows either Microsoft.Diagnostics.Tracing or
3057 // System.Diagnostics.Tracing EventSource to be considered valid. This should not mattter anywhere but in Microsoft.Diagnostics.Tracing (nuget package).
3058 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3059 #endif // !ES_BUILD_PN
3061 // Let the runtime to the work for us, since we can execute code in this context.
3062 Attribute? firstAttribute = null;
3063 foreach (object attribute in member.GetCustomAttributes(attributeType, false))
3065 firstAttribute = (Attribute)attribute;
3066 break;
3068 return firstAttribute;
3071 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3072 // In the reflection only context, we have to do things by hand.
3073 string fullTypeNameToFind = attributeType.FullName!;
3075 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3076 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3077 #endif
3079 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3081 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType!))
3083 Attribute? attr = null;
3085 Debug.Assert(data.ConstructorArguments.Count <= 1);
3087 if (data.ConstructorArguments.Count == 1)
3089 attr = (Attribute?)Activator.CreateInstance(attributeType, new object?[] { data.ConstructorArguments[0].Value });
3091 else if (data.ConstructorArguments.Count == 0)
3093 attr = (Attribute?)Activator.CreateInstance(attributeType);
3096 if (attr != null)
3098 Type t = attr.GetType();
3100 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3102 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance)!;
3103 object value = namedArgument.TypedValue.Value!;
3105 if (p.PropertyType.IsEnum)
3107 string val = value.ToString()!;
3108 value = Enum.Parse(p.PropertyType, val);
3111 p.SetValue(attr, value, null);
3114 return attr;
3119 return null;
3120 #else // ES_BUILD_PCL && ES_BUILD_PN
3121 // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
3122 throw new ArgumentException("EventSource_PCLPlatformNotSupportedReflection", "EventSource");
3123 #endif
3126 /// <summary>
3127 /// Evaluates if two related "EventSource"-domain types should be considered the same
3128 /// </summary>
3129 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3130 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3131 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3132 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3133 /// </param>
3134 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3135 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3137 return
3138 // are these the same type?
3139 attributeType == reflectedAttributeType ||
3140 // are the full typenames equal?
3141 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3142 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3143 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3144 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3145 attributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
3146 (reflectedAttributeType.Namespace!.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
3147 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3148 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
3149 #endif
3153 private static Type? GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3155 Type? ret = eventSourceType;
3157 // return false for "object" and interfaces
3158 if (ret.BaseType() == null)
3159 return null;
3161 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3164 ret = ret.BaseType();
3166 while (ret != null && ret.IsAbstract());
3168 if (ret != null)
3170 if (!allowEventSourceOverride)
3172 if (reflectionOnly && ret.FullName != typeof(EventSource).FullName ||
3173 !reflectionOnly && ret != typeof(EventSource))
3174 return null;
3176 else
3178 if (ret.Name != "EventSource")
3179 return null;
3182 return ret;
3185 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3186 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3187 // at run time. 'source' is the event source to place the descriptors. If it is null,
3188 // then the descriptors are not creaed, and just the manifest is generated.
3189 private static byte[]? CreateManifestAndDescriptors(Type eventSourceType, string? eventSourceDllName, EventSource? source,
3190 EventManifestOptions flags = EventManifestOptions.None)
3192 ManifestBuilder? manifest = null;
3193 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3194 Exception? exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3195 byte[]? res = null;
3197 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3198 return null;
3202 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3203 EventAttribute defaultEventAttribute;
3204 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3205 EventMetadata[]? eventData = null;
3206 Dictionary<string, string>? eventsByName = null;
3207 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3209 eventData = new EventMetadata[methods.Length + 1];
3210 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3213 // See if we have localization information.
3214 ResourceManager? resources = null;
3215 EventSourceAttribute? eventSourceAttrib = (EventSourceAttribute?)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3216 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3217 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3219 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3220 resources, flags);
3222 // Add an entry unconditionally for event ID 0 which will be for a string message.
3223 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3224 manifest.AddEventParameter(typeof(string), "message");
3225 manifest.EndEvent();
3227 // eventSourceType must be sealed and must derive from this EventSource
3228 if ((flags & EventManifestOptions.Strict) != 0)
3230 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3232 if (!typeMatch)
3234 manifest.ManifestError(SR.EventSource_TypeMustDeriveFromEventSource);
3236 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3238 manifest.ManifestError(SR.EventSource_TypeMustBeSealedOrAbstract);
3242 // Collect task, opcode, keyword and channel information
3243 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3244 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3245 #else
3246 foreach (string providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3247 #endif
3249 Type? nestedType = eventSourceType.GetNestedType(providerEnumKind);
3250 if (nestedType != null)
3252 if (eventSourceType.IsAbstract())
3254 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareKTOC, nestedType.Name));
3256 else
3258 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3260 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3265 // ensure we have keywords for the session-filtering reserved bits
3267 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3268 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3269 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3270 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3273 if (eventSourceType != typeof(EventSource))
3275 for (int i = 0; i < methods.Length; i++)
3277 MethodInfo method = methods[i];
3278 ParameterInfo[] args = method.GetParameters();
3280 // Get the EventDescriptor (from the Custom attributes)
3281 EventAttribute? eventAttribute = (EventAttribute?)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3283 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3284 // the only reason of limiting the number of methods considered to be events. This broke a common
3285 // design of having event sources implement specific interfaces. To fix this in a compatible way
3286 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3287 // as they are marked with the [Event] attribute
3288 if (/* method.IsVirtual || */ method.IsStatic)
3290 continue;
3293 if (eventSourceType.IsAbstract())
3295 if (eventAttribute != null)
3297 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareEventMethods, method.Name, eventAttribute.EventId));
3299 continue;
3301 else if (eventAttribute == null)
3303 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3304 // (see Compat comment above)
3305 if (method.ReturnType != typeof(void))
3307 continue;
3310 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3311 // (see Compat comment above)
3312 if (method.IsVirtual)
3314 continue;
3317 // If we explicitly mark the method as not being an event, then honor that.
3318 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3319 continue;
3321 defaultEventAttribute = new EventAttribute(eventId);
3322 eventAttribute = defaultEventAttribute;
3324 else if (eventAttribute.EventId <= 0)
3326 manifest.ManifestError(SR.Format(SR.EventSource_NeedPositiveId, method.Name), true);
3327 continue; // don't validate anything else for this event
3329 if (method.Name.LastIndexOf('.') >= 0)
3331 manifest.ManifestError(SR.Format(SR.EventSource_EventMustNotBeExplicitImplementation, method.Name, eventAttribute.EventId));
3334 eventId++;
3335 string eventName = method.Name;
3337 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3339 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3340 bool noTask = (eventAttribute.Task == EventTask.None);
3341 if (noTask)
3342 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3344 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3345 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3346 if (!eventAttribute.IsOpcodeSet)
3347 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3349 // Make the stop opcode have the same task as the start opcode.
3350 if (noTask)
3352 if (eventAttribute.Opcode == EventOpcode.Start)
3354 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3355 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3356 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3358 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3359 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3360 manifest.AddTask(taskName, (int)eventAttribute.Task);
3363 else if (eventAttribute.Opcode == EventOpcode.Stop)
3365 // Find the start associated with this stop event. We require start to be immediately before the stop
3366 int startEventId = eventAttribute.EventId - 1;
3367 if (eventData != null && startEventId < eventData.Length)
3369 Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3370 EventMetadata startEventMetadata = eventData[startEventId];
3372 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3373 // Ideally we would throw an error
3374 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3375 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3376 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3377 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3380 // Make the stop event match the start event
3381 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3382 noTask = false;
3385 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3387 throw new ArgumentException(SR.EventSource_StopsFollowStarts);
3393 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3394 if (!(source != null && source.SelfDescribingEvents))
3396 manifest.StartEvent(eventName, eventAttribute);
3397 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3399 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name!);
3401 manifest.EndEvent();
3404 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3406 Debug.Assert(eventData != null);
3407 // Do checking for user errors (optional, but not a big deal so we do it).
3408 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3410 #if FEATURE_MANAGED_ETW_CHANNELS
3411 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3412 // and is not required for the manifest
3413 if (eventAttribute.Channel != EventChannel.None)
3415 unchecked
3417 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3420 #endif
3421 string eventKey = "event_" + eventName;
3422 string? msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3423 // overwrite inline message with the localized message
3424 if (msg != null) eventAttribute.Message = msg;
3426 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3431 // Tell the TraceLogging stuff where to start allocating its own IDs.
3432 NameInfo.ReserveEventIDsBelow(eventId);
3434 if (source != null)
3436 Debug.Assert(eventData != null);
3437 TrimEventDescriptors(ref eventData);
3438 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3439 #if FEATURE_MANAGED_ETW_CHANNELS
3440 source.m_channelData = manifest.GetChannelData();
3441 #endif
3444 // if this is an abstract event source we've already performed all the validation we can
3445 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3447 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3448 #if FEATURE_MANAGED_ETW_CHANNELS
3449 || manifest.GetChannelData().Length > 0
3450 #endif
3453 // if the manifest is not needed and we're not requested to validate the event source return early
3454 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3455 return null;
3457 res = manifest.CreateManifest();
3460 catch (Exception e)
3462 // if this is a runtime manifest generation let the exception propagate
3463 if ((flags & EventManifestOptions.Strict) == 0)
3464 throw;
3465 // else store it to include it in the Argument exception we raise below
3466 exception = e;
3469 if ((flags & EventManifestOptions.Strict) != 0 && (manifest?.Errors.Count > 0 || exception != null))
3471 string msg = string.Empty;
3473 if (manifest?.Errors.Count > 0)
3475 bool firstError = true;
3476 foreach (string error in manifest.Errors)
3478 if (!firstError)
3479 msg += Environment.NewLine;
3480 firstError = false;
3481 msg += error;
3484 else
3485 msg = "Unexpected error: " + exception!.Message;
3487 throw new ArgumentException(msg, exception);
3490 return bNeedsManifest ? res : null;
3493 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3495 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3496 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3497 string.Equals(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase))
3499 var newargs = new ParameterInfo[args.Length - 1];
3500 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3501 args = newargs;
3503 return true;
3506 return false;
3509 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3510 // to the manifest.
3511 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3513 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3514 Type staticFieldType = staticField.FieldType;
3515 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3517 if (providerEnumKind != "Opcodes") goto Error;
3518 int value = (int)staticField.GetRawConstantValue()!;
3519 manifest.AddOpcode(staticField.Name, value);
3521 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3523 if (providerEnumKind != "Tasks") goto Error;
3524 int value = (int)staticField.GetRawConstantValue()!;
3525 manifest.AddTask(staticField.Name, value);
3527 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3529 if (providerEnumKind != "Keywords") goto Error;
3530 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue()!);
3531 manifest.AddKeyword(staticField.Name, value);
3533 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3534 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3536 if (providerEnumKind != "Channels") goto Error;
3537 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3538 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3540 #endif
3541 return;
3542 Error:
3543 manifest.ManifestError(SR.Format(SR.EventSource_EnumKindMismatch, staticField.Name, staticField.FieldType.Name, providerEnumKind));
3546 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3547 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3548 // it is populated if we need to look up message resources
3549 private static void AddEventDescriptor(
3550 [NotNull] ref EventMetadata[] eventData,
3551 string eventName,
3552 EventAttribute eventAttribute,
3553 ParameterInfo[] eventParameters,
3554 bool hasRelatedActivityID)
3556 if (eventData.Length <= eventAttribute.EventId)
3558 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3559 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3560 eventData = newValues;
3563 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3564 eventAttribute.EventId,
3565 eventAttribute.Version,
3566 #if FEATURE_MANAGED_ETW_CHANNELS
3567 (byte)eventAttribute.Channel,
3568 #else
3569 (byte)0,
3570 #endif
3571 (byte)eventAttribute.Level,
3572 (byte)eventAttribute.Opcode,
3573 (int)eventAttribute.Task,
3574 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3576 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3577 eventData[eventAttribute.EventId].Name = eventName;
3578 eventData[eventAttribute.EventId].Parameters = eventParameters;
3579 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3580 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3581 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3582 eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero;
3585 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3586 // size after all event descriptors have been added.
3587 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3589 int idx = eventData.Length;
3590 while (0 < idx)
3592 --idx;
3593 if (eventData[idx].Descriptor.EventId != 0)
3594 break;
3596 if (eventData.Length - idx > 2) // allow one wasted slot.
3598 EventMetadata[] newValues = new EventMetadata[idx + 1];
3599 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3600 eventData = newValues;
3604 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3605 // when a listener gets attached to a eventSource
3606 internal void AddListener(EventListener listener)
3608 lock (EventListener.EventListenersLock)
3610 bool[]? enabledArray = null;
3611 if (m_eventData != null)
3612 enabledArray = new bool[m_eventData.Length];
3613 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3614 listener.OnEventSourceCreated(this);
3618 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3619 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3620 private static void DebugCheckEvent(ref Dictionary<string, string>? eventsByName,
3621 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3622 ManifestBuilder manifest, EventManifestOptions options)
3624 int evtId = eventAttribute.EventId;
3625 string evtName = method.Name;
3626 int eventArg = GetHelperCallFirstArg(method);
3627 if (eventArg >= 0 && evtId != eventArg)
3629 manifest.ManifestError(SR.Format(SR.EventSource_MismatchIdToWriteEvent, evtName, evtId, eventArg), true);
3632 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3634 manifest.ManifestError(SR.Format(SR.EventSource_EventIdReused, evtName, evtId, eventData[evtId].Name), true);
3637 // We give a task to things if they don't have one.
3638 // TODO this is moderately expensive (N*N). We probably should not even bother....
3639 Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3640 for (int idx = 0; idx < eventData.Length; ++idx)
3642 // skip unused Event IDs.
3643 if (eventData[idx].Name == null)
3644 continue;
3646 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3648 manifest.ManifestError(SR.Format(SR.EventSource_TaskOpcodePairReused,
3649 evtName, evtId, eventData[idx].Name, idx));
3650 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3651 if ((options & EventManifestOptions.Strict) == 0)
3652 break;
3656 // for non-default event opcodes the user must define a task!
3657 if (eventAttribute.Opcode != EventOpcode.Info)
3659 bool failure = false;
3660 if (eventAttribute.Task == EventTask.None)
3661 failure = true;
3662 else
3664 // If you have the auto-assigned Task, then you did not explicitly set one.
3665 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3666 // But all other cases we want to catch the omission.
3667 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3668 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3669 failure = true;
3671 if (failure)
3673 manifest.ManifestError(SR.Format(SR.EventSource_EventMustHaveTaskIfNonDefaultOpcode, evtName, evtId));
3677 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3678 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3679 // by eventRegister.exe)
3680 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3681 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3682 // {
3683 // throw new WarningException(SR.EventSource_EventNameDoesNotEqualTaskPlusOpcode);
3684 // }
3686 if (eventsByName == null)
3687 eventsByName = new Dictionary<string, string>();
3689 if (eventsByName.ContainsKey(evtName))
3691 manifest.ManifestError(SR.Format(SR.EventSource_EventNameReused, evtName), true);
3694 eventsByName[evtName] = evtName;
3697 /// <summary>
3698 /// This method looks at the IL and tries to pattern match against the standard
3699 /// 'boilerplate' event body
3700 /// <code>
3701 /// { if (Enabled()) WriteEvent(#, ...) }
3702 /// </code>
3703 /// If the pattern matches, it returns the literal number passed as the first parameter to
3704 /// the WriteEvent. This is used to find common user errors (mismatching this
3705 /// number with the EventAttribute ID). It is only used for validation.
3706 /// </summary>
3707 /// <param name="method">The method to probe.</param>
3708 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3709 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3710 private static int GetHelperCallFirstArg(MethodInfo method)
3712 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3713 // Currently searches for the following pattern
3715 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3716 // LDARG0
3717 // LDC.I4 XXX
3718 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3719 // CALL
3720 // NOP // 0 or more times
3721 // RET
3723 // If we find this pattern we return the XXX. Otherwise we return -1.
3724 #if ES_BUILD_STANDALONE
3725 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3726 #endif
3727 byte[] instrs = method.GetMethodBody()!.GetILAsByteArray()!;
3728 int retVal = -1;
3729 for (int idx = 0; idx < instrs.Length;)
3731 switch (instrs[idx])
3733 case 0: // NOP
3734 case 1: // BREAK
3735 case 2: // LDARG_0
3736 case 3: // LDARG_1
3737 case 4: // LDARG_2
3738 case 5: // LDARG_3
3739 case 6: // LDLOC_0
3740 case 7: // LDLOC_1
3741 case 8: // LDLOC_2
3742 case 9: // LDLOC_3
3743 case 10: // STLOC_0
3744 case 11: // STLOC_1
3745 case 12: // STLOC_2
3746 case 13: // STLOC_3
3747 break;
3748 case 14: // LDARG_S
3749 case 16: // STARG_S
3750 idx++;
3751 break;
3752 case 20: // LDNULL
3753 break;
3754 case 21: // LDC_I4_M1
3755 case 22: // LDC_I4_0
3756 case 23: // LDC_I4_1
3757 case 24: // LDC_I4_2
3758 case 25: // LDC_I4_3
3759 case 26: // LDC_I4_4
3760 case 27: // LDC_I4_5
3761 case 28: // LDC_I4_6
3762 case 29: // LDC_I4_7
3763 case 30: // LDC_I4_8
3764 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3765 retVal = instrs[idx] - 22;
3766 break;
3767 case 31: // LDC_I4_S
3768 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3769 retVal = instrs[idx + 1];
3770 idx++;
3771 break;
3772 case 32: // LDC_I4
3773 idx += 4;
3774 break;
3775 case 37: // DUP
3776 break;
3777 case 40: // CALL
3778 idx += 4;
3780 if (retVal >= 0)
3782 // Is this call just before return?
3783 for (int search = idx + 1; search < instrs.Length; search++)
3785 if (instrs[search] == 42) // RET
3786 return retVal;
3787 if (instrs[search] != 0) // NOP
3788 break;
3791 retVal = -1;
3792 break;
3793 case 44: // BRFALSE_S
3794 case 45: // BRTRUE_S
3795 retVal = -1;
3796 idx++;
3797 break;
3798 case 57: // BRFALSE
3799 case 58: // BRTRUE
3800 retVal = -1;
3801 idx += 4;
3802 break;
3803 case 103: // CONV_I1
3804 case 104: // CONV_I2
3805 case 105: // CONV_I4
3806 case 106: // CONV_I8
3807 case 109: // CONV_U4
3808 case 110: // CONV_U8
3809 break;
3810 case 140: // BOX
3811 case 141: // NEWARR
3812 idx += 4;
3813 break;
3814 case 162: // STELEM_REF
3815 break;
3816 case 254: // PREFIX
3817 idx++;
3818 // Covers the CEQ instructions used in debug code for some reason.
3819 if (idx >= instrs.Length || instrs[idx] >= 6)
3820 goto default;
3821 break;
3822 default:
3823 /* Debug.Fail("Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3824 " at " + idx + " in method " + method.Name); */
3825 return -1;
3827 idx++;
3829 #endif
3830 return -1;
3833 /// <summary>
3834 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3835 /// It will do this even if the EventSource is not enabled.
3836 /// TODO remove flush parameter it is not used.
3837 /// </summary>
3838 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3839 internal void ReportOutOfBandMessage(string msg, bool flush)
3843 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3844 // send message to debugger without delay
3845 System.Diagnostics.Debugger.Log(0, null, string.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
3846 #endif
3848 // Send it to all listeners.
3849 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3850 m_outOfBandMessageCount++;
3851 else
3853 if (m_outOfBandMessageCount == 16)
3854 return;
3855 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3856 msg = "Reached message limit. End of EventSource error messages.";
3859 WriteEventString(EventLevel.LogAlways, -1, msg);
3860 WriteStringToAllListeners("EventSourceMessage", msg);
3862 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3865 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3867 EventSourceSettings evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3868 EventSourceSettings.EtwSelfDescribingEventFormat;
3869 if ((settings & evtFormatMask) == evtFormatMask)
3871 throw new ArgumentException(SR.EventSource_InvalidEventFormat, nameof(settings));
3874 // If you did not explicitly ask for manifest, you get self-describing.
3875 if ((settings & evtFormatMask) == 0)
3876 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3877 return settings;
3880 private bool ThrowOnEventWriteErrors => (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0;
3882 private bool SelfDescribingEvents
3886 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
3887 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
3888 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
3892 // private instance state
3893 private string m_name = null!; // My friendly name (privided in ctor)
3894 internal int m_id; // A small integer that is unique to this instance.
3895 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
3896 internal volatile EventMetadata[]? m_eventData; // None per-event data
3897 private volatile byte[]? m_rawManifest; // Bytes to send out representing the event schema
3899 private EventHandler<EventCommandEventArgs>? m_eventCommandExecuted;
3901 private readonly EventSourceSettings m_config; // configuration information
3903 private bool m_eventSourceDisposed; // has Dispose been called.
3905 // Enabling bits
3906 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
3907 internal EventLevel m_level; // highest level enabled by any output dispatcher
3908 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
3910 // Dispatching state
3911 internal volatile EventDispatcher? m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
3912 #if FEATURE_MANAGED_ETW
3913 private volatile OverideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
3914 #endif
3915 #if FEATURE_PERFTRACING
3916 private volatile OverideEventProvider m_eventPipeProvider = null!;
3917 #endif
3918 private bool m_completelyInited; // The EventSource constructor has returned without exception.
3919 private Exception? m_constructionException; // If there was an exception construction, this is it
3920 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
3921 private EventCommandEventArgs? m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
3923 private string[]? m_traits; // Used to implement GetTraits
3925 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
3926 [ThreadStatic]
3927 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
3929 [ThreadStatic]
3930 private static bool m_EventSourceInDecodeObject = false;
3932 #if FEATURE_MANAGED_ETW_CHANNELS
3933 internal volatile ulong[]? m_channelData;
3934 #endif
3936 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
3937 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
3938 private ActivityTracker m_activityTracker = null!;
3939 internal const string s_ActivityStartSuffix = "Start";
3940 internal const string s_ActivityStopSuffix = "Stop";
3942 // WARNING: Do not depend upon initialized statics during creation of EventSources, as it is possible for creation of an EventSource to trigger
3943 // creation of yet another EventSource. When this happens, these statics may not yet be initialized.
3944 // Rather than depending on initialized statics, use lazy initialization to ensure that the statics are initialized exactly when they are needed.
3946 // used for generating GUID from eventsource name
3947 private static byte[]? namespaceBytes;
3949 #endregion
3952 /// <summary>
3953 /// Enables specifying event source configuration options to be used in the EventSource constructor.
3954 /// </summary>
3955 [Flags]
3956 public enum EventSourceSettings
3958 /// <summary>
3959 /// This specifies none of the special configuration options should be enabled.
3960 /// </summary>
3961 Default = 0,
3962 /// <summary>
3963 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
3964 /// </summary>
3965 ThrowOnEventWriteErrors = 1,
3966 /// <summary>
3967 /// Setting this option is a directive to the ETW listener should use manifest-based format when
3968 /// firing events. This is the default option when defining a type derived from EventSource
3969 /// (using the protected EventSource constructors).
3970 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3971 /// </summary>
3972 EtwManifestEventFormat = 4,
3973 /// <summary>
3974 /// Setting this option is a directive to the ETW listener should use self-describing event format
3975 /// when firing events. This is the default option when creating a new instance of the EventSource
3976 /// type (using the public EventSource constructors).
3977 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
3978 /// </summary>
3979 EtwSelfDescribingEventFormat = 8,
3982 /// <summary>
3983 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
3984 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
3985 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
3986 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
3987 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
3988 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
3989 /// longer needed.
3990 /// <para>
3991 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
3992 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
3993 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
3994 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
3995 /// </para><para>
3996 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
3997 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
3998 /// </para><para>
3999 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4000 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4001 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4002 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4003 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4004 /// </para><para>
4005 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4006 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4007 /// that eventSource what events that dispatcher will receive.
4008 /// </para><para>
4009 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4010 /// override this method to do something useful with the data.
4011 /// </para><para>
4012 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4013 /// invariant associated with this callback is that every eventSource gets exactly one
4014 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4015 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4016 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4017 /// created.
4018 /// </para>
4019 /// </summary>
4020 public class EventListener : IDisposable
4022 private event EventHandler<EventSourceCreatedEventArgs>? _EventSourceCreated;
4024 /// <summary>
4025 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4026 /// This can happen for all existing EventSources when the EventListener is created
4027 /// as well as for any EventSources that come into existence after the EventListener
4028 /// has been created.
4030 /// These 'catch up' events are called during the construction of the EventListener.
4031 /// Subclasses need to be prepared for that.
4033 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4034 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4035 /// </summary>
4036 public event EventHandler<EventSourceCreatedEventArgs>? EventSourceCreated
4040 CallBackForExistingEventSources(false, value);
4042 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Combine(_EventSourceCreated, value);
4044 remove
4046 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>?)Delegate.Remove(_EventSourceCreated, value);
4050 /// <summary>
4051 /// This event is raised whenever an event has been written by a EventSource for which
4052 /// the EventListener has enabled events.
4053 /// </summary>
4054 public event EventHandler<EventWrittenEventArgs>? EventWritten;
4056 static EventListener()
4058 #if FEATURE_PERFTRACING
4059 // Ensure that NativeRuntimeEventSource is initialized so that EventListeners get an opportunity to subscribe to its events.
4060 // This is required because NativeRuntimeEventSource never emit events on its own, and thus will never be initialized
4061 // in the normal way that EventSources are initialized.
4062 GC.KeepAlive(NativeRuntimeEventSource.Log);
4063 #endif // FEATURE_PERFTRACING
4066 /// <summary>
4067 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4068 /// them on).
4069 /// </summary>
4070 public EventListener()
4072 // This will cause the OnEventSourceCreated callback to fire.
4073 CallBackForExistingEventSources(true, (obj, args) =>
4075 args.EventSource!.AddListener((EventListener)obj!);
4079 /// <summary>
4080 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4081 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4082 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4083 /// call Dispose when they are done with their logging.
4084 /// </summary>
4085 #if ES_BUILD_STANDALONE
4086 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4087 #endif
4088 public virtual void Dispose()
4090 lock (EventListenersLock)
4092 if (s_Listeners != null)
4094 if (this == s_Listeners)
4096 EventListener cur = s_Listeners;
4097 s_Listeners = this.m_Next;
4098 RemoveReferencesToListenerInEventSources(cur);
4100 else
4102 // Find 'this' from the s_Listeners linked list.
4103 EventListener prev = s_Listeners;
4104 for (;;)
4106 EventListener? cur = prev.m_Next;
4107 if (cur == null)
4108 break;
4109 if (cur == this)
4111 // Found our Listener, remove references to it in the eventSources
4112 prev.m_Next = cur.m_Next; // Remove entry.
4113 RemoveReferencesToListenerInEventSources(cur);
4114 break;
4116 prev = cur;
4120 Validate();
4123 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4124 // 'cleanup' associated with this object
4126 /// <summary>
4127 /// Enable all events from the eventSource identified by 'eventSource' to the current
4128 /// dispatcher that have a verbosity level of 'level' or lower.
4130 /// This call can have the effect of REDUCING the number of events sent to the
4131 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4133 /// This call never has an effect on other EventListeners.
4135 /// </summary>
4136 public void EnableEvents(EventSource eventSource, EventLevel level)
4138 EnableEvents(eventSource, level, EventKeywords.None);
4140 /// <summary>
4141 /// Enable all events from the eventSource identified by 'eventSource' to the current
4142 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4143 /// matching any of the bits in 'matchAnyKeyword'.
4145 /// This call can have the effect of REDUCING the number of events sent to the
4146 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4147 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4149 /// This call never has an effect on other EventListeners.
4150 /// </summary>
4151 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4153 EnableEvents(eventSource, level, matchAnyKeyword, null);
4155 /// <summary>
4156 /// Enable all events from the eventSource identified by 'eventSource' to the current
4157 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4158 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4159 /// effect passing additional 'key-value' arguments 'arguments' might have.
4161 /// This call can have the effect of REDUCING the number of events sent to the
4162 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4163 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4165 /// This call never has an effect on other EventListeners.
4166 /// </summary>
4167 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string?>? arguments)
4169 if (eventSource == null)
4171 throw new ArgumentNullException(nameof(eventSource));
4174 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4176 #if FEATURE_PERFTRACING
4177 if (eventSource.GetType() == typeof(NativeRuntimeEventSource))
4179 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, true, level, matchAnyKeyword);
4181 #endif // FEATURE_PERFTRACING
4183 /// <summary>
4184 /// Disables all events coming from eventSource identified by 'eventSource'.
4186 /// This call never has an effect on other EventListeners.
4187 /// </summary>
4188 public void DisableEvents(EventSource eventSource)
4190 if (eventSource == null)
4192 throw new ArgumentNullException(nameof(eventSource));
4195 eventSource.SendCommand(this, EventProviderType.None, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4197 #if FEATURE_PERFTRACING
4198 if (eventSource.GetType() == typeof(NativeRuntimeEventSource))
4200 EventPipeEventDispatcher.Instance.SendCommand(this, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None);
4202 #endif // FEATURE_PERFTRACING
4205 /// <summary>
4206 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4207 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4208 /// it useful to store additional information about each eventSource connected to it,
4209 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4210 /// (growable) array (eg List(T)).
4211 /// </summary>
4212 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4214 /// <summary>
4215 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4216 /// This can happen for all existing EventSources when the EventListener is created
4217 /// as well as for any EventSources that come into existence after the EventListener
4218 /// has been created.
4220 /// These 'catch up' events are called during the construction of the EventListener.
4221 /// Subclasses need to be prepared for that.
4223 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4224 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4225 /// </summary>
4226 /// <param name="eventSource"></param>
4227 protected internal virtual void OnEventSourceCreated(EventSource eventSource)
4229 EventHandler<EventSourceCreatedEventArgs>? callBack = this._EventSourceCreated;
4230 if (callBack != null)
4232 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4233 args.EventSource = eventSource;
4234 callBack(this, args);
4238 /// <summary>
4239 /// This method is called whenever an event has been written by a EventSource for which
4240 /// the EventListener has enabled events.
4241 /// </summary>
4242 /// <param name="eventData"></param>
4243 protected internal virtual void OnEventWritten(EventWrittenEventArgs eventData)
4245 this.EventWritten?.Invoke(this, eventData);
4249 #region private
4250 /// <summary>
4251 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4252 /// ID to the eventSource (which is simply the ordinal in the global list).
4254 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4255 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4256 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4257 /// that are in the list). This seems OK since the expectation is that EventSources
4258 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4259 /// global variables).
4260 /// </summary>
4261 /// <param name="newEventSource"></param>
4262 internal static void AddEventSource(EventSource newEventSource)
4264 lock (EventListenersLock)
4266 if (s_EventSources == null)
4267 s_EventSources = new List<WeakReference>(2);
4269 if (!s_EventSourceShutdownRegistered)
4271 s_EventSourceShutdownRegistered = true;
4272 #if ES_BUILD_STANDALONE
4273 AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
4274 AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
4275 #else
4276 AppContext.ProcessExit += DisposeOnShutdown;
4277 #endif
4281 // Periodically search the list for existing entries to reuse, this avoids
4282 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4283 int newIndex = -1;
4284 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4286 int i = s_EventSources.Count; // Work from the top down.
4287 while (0 < i)
4289 --i;
4290 WeakReference weakRef = s_EventSources[i];
4291 if (!weakRef.IsAlive)
4293 newIndex = i;
4294 weakRef.Target = newEventSource;
4295 break;
4299 if (newIndex < 0)
4301 newIndex = s_EventSources.Count;
4302 s_EventSources.Add(new WeakReference(newEventSource));
4304 newEventSource.m_id = newIndex;
4306 #if DEBUG
4307 // Disable validation of EventSource/EventListener connections in case a call to EventSource.AddListener
4308 // causes a recursive call into this method.
4309 bool previousValue = s_ConnectingEventSourcesAndListener;
4310 s_ConnectingEventSourcesAndListener = true;
4313 #endif
4314 // Add every existing dispatcher to the new EventSource
4315 for (EventListener? listener = s_Listeners; listener != null; listener = listener.m_Next)
4316 newEventSource.AddListener(listener);
4317 #if DEBUG
4319 finally
4321 s_ConnectingEventSourcesAndListener = previousValue;
4323 #endif
4325 Validate();
4329 // Whenver we have async callbacks from native code, there is an ugly issue where
4330 // during .NET shutdown native code could be calling the callback, but the CLR
4331 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4332 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4333 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4334 // do this. This is what this callback is for.
4335 // See bug 724140 for more
4336 private static void DisposeOnShutdown(object? sender, EventArgs e)
4338 lock (EventListenersLock)
4340 Debug.Assert(s_EventSources != null);
4341 foreach (WeakReference esRef in s_EventSources)
4343 if (esRef.Target is EventSource es)
4344 es.Dispose();
4349 /// <summary>
4350 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4351 /// eventSources in the appdomain.
4353 /// The EventListenersLock must be held before calling this routine.
4354 /// </summary>
4355 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4357 #if !ES_BUILD_STANDALONE
4358 Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4359 #endif
4360 // Foreach existing EventSource in the appdomain
4361 Debug.Assert(s_EventSources != null);
4362 foreach (WeakReference eventSourceRef in s_EventSources)
4364 if (eventSourceRef.Target is EventSource eventSource)
4366 Debug.Assert(eventSource.m_Dispatchers != null);
4367 // Is the first output dispatcher the dispatcher we are removing?
4368 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4369 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4370 else
4372 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4373 EventDispatcher? prev = eventSource.m_Dispatchers;
4374 for (;;)
4376 EventDispatcher? cur = prev.m_Next;
4377 if (cur == null)
4379 Debug.Fail("EventSource did not have a registered EventListener!");
4380 break;
4382 if (cur.m_Listener == listenerToRemove)
4384 prev.m_Next = cur.m_Next; // Remove entry.
4385 break;
4387 prev = cur;
4393 #if FEATURE_PERFTRACING
4394 // Remove the listener from the EventPipe dispatcher.
4395 EventPipeEventDispatcher.Instance.RemoveEventListener(listenerToRemove);
4396 #endif // FEATURE_PERFTRACING
4399 /// <summary>
4400 /// Checks internal consistency of EventSources/Listeners.
4401 /// </summary>
4402 [Conditional("DEBUG")]
4403 internal static void Validate()
4405 #if DEBUG
4406 // Don't run validation code if we're in the middle of modifying the connections between EventSources and EventListeners.
4407 if (s_ConnectingEventSourcesAndListener)
4409 return;
4411 #endif
4413 lock (EventListenersLock)
4415 Debug.Assert(s_EventSources != null);
4416 // Get all listeners
4417 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4418 EventListener? cur = s_Listeners;
4419 while (cur != null)
4421 allListeners.Add(cur, true);
4422 cur = cur.m_Next;
4425 // For all eventSources
4426 int id = -1;
4427 foreach (WeakReference eventSourceRef in s_EventSources)
4429 id++;
4430 if (!(eventSourceRef.Target is EventSource eventSource))
4431 continue;
4432 Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4434 // None listeners on eventSources exist in the dispatcher list.
4435 EventDispatcher? dispatcher = eventSource.m_Dispatchers;
4436 while (dispatcher != null)
4438 Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4439 dispatcher = dispatcher.m_Next;
4442 // Every dispatcher is on Dispatcher List of every eventSource.
4443 foreach (EventListener listener in allListeners.Keys)
4445 dispatcher = eventSource.m_Dispatchers;
4446 for (;;)
4448 Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4449 if (dispatcher.m_Listener == listener)
4450 break;
4451 dispatcher = dispatcher.m_Next;
4458 /// <summary>
4459 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4460 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4461 /// the lock object)
4462 /// </summary>
4463 internal static object EventListenersLock
4467 if (s_EventSources == null)
4468 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4469 return s_EventSources;
4473 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs>? callback)
4475 lock (EventListenersLock)
4477 Debug.Assert(s_EventSources != null);
4479 // Disallow creating EventListener reentrancy.
4480 if (s_CreatingListener)
4482 throw new InvalidOperationException(SR.EventSource_ListenerCreatedInsideCallback);
4487 s_CreatingListener = true;
4489 if (addToListenersList)
4491 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4492 // Those added sources see this listener.
4493 this.m_Next = s_Listeners;
4494 s_Listeners = this;
4497 if (callback != null)
4499 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4500 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4501 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4502 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4503 // is created.
4504 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4506 #if DEBUG
4507 bool previousValue = s_ConnectingEventSourcesAndListener;
4508 s_ConnectingEventSourcesAndListener = true;
4511 #endif
4512 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4514 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4515 if (eventSourceRef.Target is EventSource eventSource)
4517 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4518 args.EventSource = eventSource;
4519 callback(this, args);
4522 #if DEBUG
4524 finally
4526 s_ConnectingEventSourcesAndListener = previousValue;
4528 #endif
4531 Validate();
4533 finally
4535 s_CreatingListener = false;
4541 // Instance fields
4542 internal volatile EventListener? m_Next; // These form a linked list in s_Listeners
4544 // static fields
4546 /// <summary>
4547 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4548 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4549 /// </summary>
4550 internal static EventListener? s_Listeners;
4551 /// <summary>
4552 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4553 /// remove themselves from this list this is a weak list and the GC that removes them may
4554 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4555 /// to filter those out.
4556 /// </summary>
4557 internal static List<WeakReference>? s_EventSources;
4559 /// <summary>
4560 /// Used to disallow reentrancy.
4561 /// </summary>
4562 private static bool s_CreatingListener = false;
4564 #if DEBUG
4565 /// <summary>
4566 /// Used to disable validation of EventSource and EventListener connectivity.
4567 /// This is needed when an EventListener is in the middle of being published to all EventSources
4568 /// and another EventSource is created as part of the process.
4569 /// </summary>
4570 [ThreadStatic]
4571 private static bool s_ConnectingEventSourcesAndListener = false;
4572 #endif
4574 /// <summary>
4575 /// Used to register AD/Process shutdown callbacks.
4576 /// </summary>
4577 private static bool s_EventSourceShutdownRegistered = false;
4578 #endregion
4581 /// <summary>
4582 /// Passed to the code:EventSource.OnEventCommand callback
4583 /// </summary>
4584 public class EventCommandEventArgs : EventArgs
4586 /// <summary>
4587 /// Gets the command for the callback.
4588 /// </summary>
4589 public EventCommand Command { get; internal set; }
4591 /// <summary>
4592 /// Gets the arguments for the callback.
4593 /// </summary>
4594 public IDictionary<string, string?>? Arguments { get; internal set; }
4596 /// <summary>
4597 /// Enables the event that has the specified identifier.
4598 /// </summary>
4599 /// <param name="eventId">Event ID of event to be enabled</param>
4600 /// <returns>true if eventId is in range</returns>
4601 public bool EnableEvent(int eventId)
4603 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4604 throw new InvalidOperationException();
4605 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, true);
4608 /// <summary>
4609 /// Disables the event that have the specified identifier.
4610 /// </summary>
4611 /// <param name="eventId">Event ID of event to be disabled</param>
4612 /// <returns>true if eventId is in range</returns>
4613 public bool DisableEvent(int eventId)
4615 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4616 throw new InvalidOperationException();
4617 return eventSource.EnableEventForDispatcher(dispatcher, eventProviderType, eventId, false);
4620 #region private
4622 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string?>? arguments, EventSource eventSource,
4623 EventListener? listener, EventProviderType eventProviderType, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4625 this.Command = command;
4626 this.Arguments = arguments;
4627 this.eventSource = eventSource;
4628 this.listener = listener;
4629 this.eventProviderType = eventProviderType;
4630 this.perEventSourceSessionId = perEventSourceSessionId;
4631 this.etwSessionId = etwSessionId;
4632 this.enable = enable;
4633 this.level = level;
4634 this.matchAnyKeyword = matchAnyKeyword;
4637 internal EventSource eventSource;
4638 internal EventDispatcher? dispatcher;
4639 internal EventProviderType eventProviderType;
4641 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4642 internal EventListener? listener;
4643 internal int perEventSourceSessionId;
4644 internal int etwSessionId;
4645 internal bool enable;
4646 internal EventLevel level;
4647 internal EventKeywords matchAnyKeyword;
4648 internal EventCommandEventArgs? nextCommand; // We form a linked list of these deferred commands.
4650 #endregion
4653 /// <summary>
4654 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4655 /// </summary>
4656 public class EventSourceCreatedEventArgs : EventArgs
4658 /// <summary>
4659 /// The EventSource that is attaching to the listener.
4660 /// </summary>
4661 public EventSource? EventSource
4663 get;
4664 internal set;
4668 /// <summary>
4669 /// EventWrittenEventArgs is passed to the user-provided override for
4670 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4671 /// </summary>
4672 public class EventWrittenEventArgs : EventArgs
4674 /// <summary>
4675 /// The name of the event.
4676 /// </summary>
4677 public string? EventName
4681 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4683 return m_eventName;
4685 else
4687 Debug.Assert(m_eventSource.m_eventData != null);
4688 return m_eventSource.m_eventData[EventId].Name;
4691 internal set
4693 m_eventName = value;
4697 /// <summary>
4698 /// Gets the event ID for the event that was written.
4699 /// </summary>
4700 public int EventId { get; internal set; }
4702 /// <summary>
4703 /// Gets the activity ID for the thread on which the event was written.
4704 /// </summary>
4705 public Guid ActivityId
4709 Guid activityId = m_activityId;
4710 if (activityId == Guid.Empty)
4712 activityId = EventSource.CurrentThreadActivityId;
4715 return activityId;
4717 internal set
4719 m_activityId = value;
4723 /// <summary>
4724 /// Gets the related activity ID if one was specified when the event was written.
4725 /// </summary>
4726 public Guid RelatedActivityId
4728 get;
4729 internal set;
4732 /// <summary>
4733 /// Gets the payload for the event.
4734 /// </summary>
4735 public ReadOnlyCollection<object?>? Payload { get; internal set; }
4737 /// <summary>
4738 /// Gets the payload argument names.
4739 /// </summary>
4740 public ReadOnlyCollection<string>? PayloadNames
4744 // For contract based events we create the list lazily.
4745 // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only
4746 // do the lazy init if you know it is contract based (EventID >= 0)
4747 if (EventId >= 0 && m_payloadNames == null)
4750 var names = new List<string>();
4751 Debug.Assert(m_eventSource.m_eventData != null);
4752 foreach (ParameterInfo parameter in m_eventSource.m_eventData[EventId].Parameters)
4754 names.Add(parameter.Name!);
4757 m_payloadNames = new ReadOnlyCollection<string>(names);
4760 return m_payloadNames;
4763 internal set
4765 m_payloadNames = value;
4769 /// <summary>
4770 /// Gets the event source object.
4771 /// </summary>
4772 public EventSource EventSource => m_eventSource;
4774 /// <summary>
4775 /// Gets the keywords for the event.
4776 /// </summary>
4777 public EventKeywords Keywords
4781 if (EventId < 0) // TraceLogging convention EventID == -1
4782 return m_keywords;
4784 Debug.Assert(m_eventSource.m_eventData != null);
4785 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4789 /// <summary>
4790 /// Gets the operation code for the event.
4791 /// </summary>
4792 public EventOpcode Opcode
4796 if (EventId <= 0) // TraceLogging convention EventID == -1
4797 return m_opcode;
4799 Debug.Assert(m_eventSource.m_eventData != null);
4800 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4804 /// <summary>
4805 /// Gets the task for the event.
4806 /// </summary>
4807 public EventTask Task
4811 if (EventId <= 0) // TraceLogging convention EventID == -1
4812 return EventTask.None;
4814 Debug.Assert(m_eventSource.m_eventData != null);
4815 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4819 /// <summary>
4820 /// Any provider/user defined options associated with the event.
4821 /// </summary>
4822 public EventTags Tags
4826 if (EventId <= 0) // TraceLogging convention EventID == -1
4827 return m_tags;
4829 Debug.Assert(m_eventSource.m_eventData != null);
4830 return m_eventSource.m_eventData[EventId].Tags;
4834 /// <summary>
4835 /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
4836 /// </summary>
4837 public string? Message
4841 if (EventId <= 0) // TraceLogging convention EventID == -1
4843 return m_message;
4845 else
4847 Debug.Assert(m_eventSource.m_eventData != null);
4848 return m_eventSource.m_eventData[EventId].Message;
4851 internal set
4853 m_message = value;
4858 #if FEATURE_MANAGED_ETW_CHANNELS
4859 /// <summary>
4860 /// Gets the channel for the event.
4861 /// </summary>
4862 public EventChannel Channel
4866 if (EventId <= 0) // TraceLogging convention EventID == -1
4867 return EventChannel.None;
4869 Debug.Assert(m_eventSource.m_eventData != null);
4870 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4873 #endif
4875 /// <summary>
4876 /// Gets the version of the event.
4877 /// </summary>
4878 public byte Version
4882 if (EventId <= 0) // TraceLogging convention EventID == -1
4883 return 0;
4885 Debug.Assert(m_eventSource.m_eventData != null);
4886 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4890 /// <summary>
4891 /// Gets the level for the event.
4892 /// </summary>
4893 public EventLevel Level
4897 if (EventId <= 0) // TraceLogging convention EventID == -1
4898 return m_level;
4900 Debug.Assert(m_eventSource.m_eventData != null);
4901 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4905 /// <summary>
4906 /// Gets the identifier for the OS thread that wrote the event.
4907 /// </summary>
4908 public long OSThreadId
4912 if (!m_osThreadId.HasValue)
4914 #if ES_BUILD_STANDALONE
4915 m_osThreadId = (long)Interop.Kernel32.GetCurrentThreadId();
4916 #else
4917 m_osThreadId = (long)Thread.CurrentOSThreadId;
4918 #endif
4921 return m_osThreadId.Value;
4923 internal set
4925 m_osThreadId = value;
4929 /// <summary>
4930 /// Gets a UTC DateTime that specifies when the event was written.
4931 /// </summary>
4932 public DateTime TimeStamp
4934 get;
4935 internal set;
4938 #region private
4939 internal EventWrittenEventArgs(EventSource eventSource)
4941 m_eventSource = eventSource;
4942 TimeStamp = DateTime.UtcNow;
4944 private string? m_message;
4945 private string? m_eventName;
4946 private readonly EventSource m_eventSource;
4947 private ReadOnlyCollection<string>? m_payloadNames;
4948 private Guid m_activityId;
4949 private long? m_osThreadId;
4950 internal EventTags m_tags;
4951 internal EventOpcode m_opcode;
4952 internal EventLevel m_level;
4953 internal EventKeywords m_keywords;
4954 #endregion
4957 /// <summary>
4958 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4959 /// </summary>
4960 [AttributeUsage(AttributeTargets.Class)]
4961 public sealed class EventSourceAttribute : Attribute
4963 /// <summary>
4964 /// Overrides the ETW name of the event source (which defaults to the class name)
4965 /// </summary>
4966 public string? Name { get; set; }
4968 /// <summary>
4969 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
4970 /// except when upgrading existing ETW providers to using event sources.
4971 /// </summary>
4972 public string? Guid { get; set; }
4974 /// <summary>
4975 /// <para>
4976 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
4977 /// can be localized to several languages if desired. This works by creating a ResX style string table
4978 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
4979 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
4980 /// resources. This name is the value of the LocalizationResources property.
4981 /// </para><para>
4982 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
4983 /// using the following resource naming scheme
4984 /// </para>
4985 /// <para>* event_EVENTNAME</para>
4986 /// <para>* task_TASKNAME</para>
4987 /// <para>* keyword_KEYWORDNAME</para>
4988 /// <para>* map_MAPNAME</para>
4989 /// <para>
4990 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
4991 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
4992 /// which represent the payload values.
4993 /// </para>
4994 /// </summary>
4995 public string? LocalizationResources { get; set; }
4998 /// <summary>
4999 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5000 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5001 /// name of the method and its signature to generate basic schema information for the event. The
5002 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5003 /// desired.
5004 /// </summary>
5005 [AttributeUsage(AttributeTargets.Method)]
5006 public sealed class EventAttribute : Attribute
5008 /// <summary>Construct an EventAttribute with specified eventId</summary>
5009 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
5010 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5011 /// <summary>Event's ID</summary>
5012 public int EventId { get; private set; }
5013 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5014 public EventLevel Level { get; set; }
5015 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5016 public EventKeywords Keywords { get; set; }
5017 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5018 public EventOpcode Opcode
5022 return m_opcode;
5026 this.m_opcode = value;
5027 this.m_opcodeSet = true;
5031 internal bool IsOpcodeSet => m_opcodeSet;
5033 /// <summary>Event's task: allows logical grouping of events</summary>
5034 public EventTask Task { get; set; }
5035 #if FEATURE_MANAGED_ETW_CHANNELS
5036 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5037 public EventChannel Channel { get; set; }
5038 #endif
5039 /// <summary>Event's version</summary>
5040 public byte Version { get; set; }
5042 /// <summary>
5043 /// This can be specified to enable formatting and localization of the event's payload. You can
5044 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5045 /// with the 'ToString()' of the corresponding part of the event payload.
5046 /// </summary>
5047 public string? Message { get; set; }
5049 /// <summary>
5050 /// User defined options associated with the event. These do not have meaning to the EventSource but
5051 /// are passed through to listeners which given them semantics.
5052 /// </summary>
5053 public EventTags Tags { get; set; }
5055 /// <summary>
5056 /// Allows fine control over the Activity IDs generated by start and stop events
5057 /// </summary>
5058 public EventActivityOptions ActivityOptions { get; set; }
5060 #region private
5061 private EventOpcode m_opcode;
5062 private bool m_opcodeSet;
5063 #endregion
5066 /// <summary>
5067 /// By default all instance methods in a class that subclasses code:EventSource that and return
5068 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5069 /// the code:NonEventAttribute
5070 /// </summary>
5071 [AttributeUsage(AttributeTargets.Method)]
5072 public sealed class NonEventAttribute : Attribute
5074 /// <summary>
5075 /// Constructs a default NonEventAttribute
5076 /// </summary>
5077 public NonEventAttribute() { }
5080 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5081 #if FEATURE_MANAGED_ETW_CHANNELS
5082 /// <summary>
5083 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5084 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5085 /// <code>
5086 /// public static class Channels
5087 /// {
5088 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5089 /// public const EventChannel Admin = (EventChannel)16;
5091 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5092 /// public const EventChannel Operational = (EventChannel)17;
5093 /// }
5094 /// </code>
5095 /// </summary>
5096 [AttributeUsage(AttributeTargets.Field)]
5097 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5098 public
5099 #else
5100 internal
5101 #endif
5102 class EventChannelAttribute : Attribute
5104 /// <summary>
5105 /// Specified whether the channel is enabled by default
5106 /// </summary>
5107 public bool Enabled { get; set; }
5109 /// <summary>
5110 /// Legal values are in EventChannelType
5111 /// </summary>
5112 public EventChannelType EventChannelType { get; set; }
5114 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5115 /// <summary>
5116 /// Specifies the isolation for the channel
5117 /// </summary>
5118 public EventChannelIsolation Isolation { get; set; }
5120 /// <summary>
5121 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5122 /// See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype) for details.
5123 /// </summary>
5124 public string? Access { get; set; }
5126 /// <summary>
5127 /// Allows importing channels defined in external manifests
5128 /// </summary>
5129 public string? ImportChannel { get; set; }
5130 #endif
5132 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
5133 // public string Name { get; set; }
5136 /// <summary>
5137 /// Allowed channel types
5138 /// </summary>
5139 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5140 public
5141 #else
5142 internal
5143 #endif
5144 enum EventChannelType
5146 /// <summary>The admin channel</summary>
5147 Admin = 1,
5148 /// <summary>The operational channel</summary>
5149 Operational,
5150 /// <summary>The Analytic channel</summary>
5151 Analytic,
5152 /// <summary>The debug channel</summary>
5153 Debug,
5156 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5157 /// <summary>
5158 /// Allowed isolation levels. See MSDN (https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-channeltype-complextype)
5159 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5160 /// access permissions for the channel and backing file.
5161 /// </summary>
5162 public
5163 enum EventChannelIsolation
5165 /// <summary>
5166 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5167 /// </summary>
5168 Application = 1,
5169 /// <summary>
5170 /// All channels that specify System isolation use the same ETW session
5171 /// </summary>
5172 System,
5173 /// <summary>
5174 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5175 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5176 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5177 /// </summary>
5178 Custom,
5180 #endif
5181 #endif
5183 /// <summary>
5184 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5185 /// </summary>
5186 public enum EventCommand
5188 /// <summary>
5189 /// Update EventSource state
5190 /// </summary>
5191 Update = 0,
5192 /// <summary>
5193 /// Request EventSource to generate and send its manifest
5194 /// </summary>
5195 SendManifest = -1,
5196 /// <summary>
5197 /// Enable event
5198 /// </summary>
5199 Enable = -2,
5200 /// <summary>
5201 /// Disable event
5202 /// </summary>
5203 Disable = -3
5207 #region private classes
5209 // holds a bitfield representing a session mask
5210 /// <summary>
5211 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5212 /// is the index in the SessionMask of the bit that will be set. These can translate to
5213 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5214 /// FromEventKeywords() methods.
5215 /// </summary>
5216 internal struct SessionMask
5218 public SessionMask(SessionMask m)
5219 { m_mask = m.m_mask; }
5221 public SessionMask(uint mask = 0)
5222 { m_mask = mask & MASK; }
5224 public bool IsEqualOrSupersetOf(SessionMask m)
5226 return (this.m_mask | m.m_mask) == this.m_mask;
5229 public static SessionMask All => new SessionMask(MASK);
5231 public static SessionMask FromId(int perEventSourceSessionId)
5233 Debug.Assert(perEventSourceSessionId < MAX);
5234 return new SessionMask((uint)1 << perEventSourceSessionId);
5237 public ulong ToEventKeywords()
5239 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5242 public static SessionMask FromEventKeywords(ulong m)
5244 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5247 public bool this[int perEventSourceSessionId]
5251 Debug.Assert(perEventSourceSessionId < MAX);
5252 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5256 Debug.Assert(perEventSourceSessionId < MAX);
5257 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5258 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5262 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5264 return new SessionMask(m1.m_mask | m2.m_mask);
5267 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5269 return new SessionMask(m1.m_mask & m2.m_mask);
5272 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5274 return new SessionMask(m1.m_mask ^ m2.m_mask);
5277 public static SessionMask operator ~(SessionMask m)
5279 return new SessionMask(MASK & ~(m.m_mask));
5282 public static explicit operator ulong(SessionMask m)
5283 { return m.m_mask; }
5285 public static explicit operator uint(SessionMask m)
5286 { return m.m_mask; }
5288 private uint m_mask;
5290 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5291 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5292 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5295 /// <summary>
5296 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5297 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5299 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5300 /// that EventListener has activate) and a Single EventSource may also have many
5301 /// event Dispatchers (one for every EventListener that has activated it).
5303 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5304 /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is
5305 /// associated with.
5306 /// </summary>
5307 internal class EventDispatcher
5309 internal EventDispatcher(EventDispatcher? next, bool[]? eventEnabled, EventListener listener)
5311 m_Next = next;
5312 m_EventEnabled = eventEnabled;
5313 m_Listener = listener;
5316 // Instance fields
5317 internal readonly EventListener m_Listener; // The dispatcher this entry is for
5318 internal bool[]? m_EventEnabled; // For every event in a the eventSource, is it enabled?
5320 // Only guaranteed to exist after a InsureInit()
5321 internal EventDispatcher? m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5322 // Of all listeners for that eventSource.
5325 /// <summary>
5326 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5327 /// generated.
5328 /// </summary>
5329 [Flags]
5330 public enum EventManifestOptions
5332 /// <summary>
5333 /// Only the resources associated with current UI culture are included in the manifest
5334 /// </summary>
5335 None = 0x0,
5336 /// <summary>
5337 /// Throw exceptions for any inconsistency encountered
5338 /// </summary>
5339 Strict = 0x1,
5340 /// <summary>
5341 /// Generate a "resources" node under "localization" for every satellite assembly provided
5342 /// </summary>
5343 AllCultures = 0x2,
5344 /// <summary>
5345 /// Generate the manifest only if the event source needs to be registered on the machine,
5346 /// otherwise return null (but still perform validation if Strict is specified)
5347 /// </summary>
5348 OnlyIfNeededForRegistration = 0x4,
5349 /// <summary>
5350 /// When generating the manifest do *not* enforce the rule that the current EventSource class
5351 /// must be the base class for the user-defined type passed in. This allows validation of .net
5352 /// event sources using the new validation code
5353 /// </summary>
5354 AllowEventSourceOverride = 0x8,
5357 /// <summary>
5358 /// ManifestBuilder is designed to isolate the details of the message of the event from the
5359 /// rest of EventSource. This one happens to create XML.
5360 /// </summary>
5361 internal class ManifestBuilder
5363 /// <summary>
5364 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
5365 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
5366 /// </summary>
5367 public ManifestBuilder(string providerName, Guid providerGuid, string? dllName, ResourceManager? resources,
5368 EventManifestOptions flags)
5370 #if FEATURE_MANAGED_ETW_CHANNELS
5371 this.providerName = providerName;
5372 #endif
5373 this.flags = flags;
5375 this.resources = resources;
5376 sb = new StringBuilder();
5377 events = new StringBuilder();
5378 templates = new StringBuilder();
5379 opcodeTab = new Dictionary<int, string>();
5380 stringTab = new Dictionary<string, string>();
5381 errors = new List<string>();
5382 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
5384 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5385 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\">");
5386 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
5387 sb.Append("<provider name=\"").Append(providerName).
5388 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
5389 if (dllName != null)
5390 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
5392 string symbolsName = providerName.Replace("-", "").Replace('.', '_'); // Period and - are illegal replace them.
5393 sb.Append("\" symbol=\"").Append(symbolsName);
5394 sb.Append("\">").AppendLine();
5397 public void AddOpcode(string name, int value)
5399 if ((flags & EventManifestOptions.Strict) != 0)
5401 if (value <= 10 || value >= 239)
5403 ManifestError(SR.Format(SR.EventSource_IllegalOpcodeValue, name, value));
5406 if (opcodeTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5408 ManifestError(SR.Format(SR.EventSource_OpcodeCollision, name, prevName, value));
5411 opcodeTab[value] = name;
5413 public void AddTask(string name, int value)
5415 if ((flags & EventManifestOptions.Strict) != 0)
5417 if (value <= 0 || value >= 65535)
5419 ManifestError(SR.Format(SR.EventSource_IllegalTaskValue, name, value));
5422 if (taskTab != null && taskTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5424 ManifestError(SR.Format(SR.EventSource_TaskCollision, name, prevName, value));
5427 if (taskTab == null)
5428 taskTab = new Dictionary<int, string>();
5429 taskTab[value] = name;
5431 public void AddKeyword(string name, ulong value)
5433 if ((value & (value - 1)) != 0) // Is it a power of 2?
5435 ManifestError(SR.Format(SR.EventSource_KeywordNeedPowerOfTwo, "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
5437 if ((flags & EventManifestOptions.Strict) != 0)
5439 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
5441 ManifestError(SR.Format(SR.EventSource_IllegalKeywordsValue, name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5444 if (keywordTab != null && keywordTab.TryGetValue(value, out string? prevName) && !name.Equals(prevName, StringComparison.Ordinal))
5446 ManifestError(SR.Format(SR.EventSource_KeywordCollision, name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
5449 if (keywordTab == null)
5450 keywordTab = new Dictionary<ulong, string>();
5451 keywordTab[value] = name;
5454 #if FEATURE_MANAGED_ETW_CHANNELS
5455 /// <summary>
5456 /// Add a channel. channelAttribute can be null
5457 /// </summary>
5458 public void AddChannel(string? name, int value, EventChannelAttribute? channelAttribute)
5460 EventChannel chValue = (EventChannel)value;
5461 if (value < (int)EventChannel.Admin || value > 255)
5462 ManifestError(SR.Format(SR.EventSource_EventChannelOutOfRange, name, value));
5463 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
5464 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
5466 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
5467 // but we want to allow them to override the default ones...
5468 ManifestError(SR.Format(SR.EventSource_ChannelTypeDoesNotMatchEventChannelValue,
5469 name, ((EventChannel)value).ToString()));
5472 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
5474 ulong kwd = GetChannelKeyword(chValue);
5476 if (channelTab == null)
5477 channelTab = new Dictionary<int, ChannelInfo>(4);
5478 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
5481 private EventChannelType EventChannelToChannelType(EventChannel channel)
5483 #if !ES_BUILD_STANDALONE
5484 Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
5485 #endif
5486 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
5488 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
5490 EventChannelAttribute attrib = new EventChannelAttribute();
5491 attrib.EventChannelType = EventChannelToChannelType(channel);
5492 if (attrib.EventChannelType <= EventChannelType.Operational)
5493 attrib.Enabled = true;
5494 return attrib;
5497 public ulong[] GetChannelData()
5499 if (this.channelTab == null)
5501 return Array.Empty<ulong>();
5504 // We create an array indexed by the channel id for fast look up.
5505 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
5506 int maxkey = -1;
5507 foreach (int item in this.channelTab.Keys)
5509 if (item > maxkey)
5511 maxkey = item;
5515 ulong[] channelMask = new ulong[maxkey + 1];
5516 foreach (KeyValuePair<int, ChannelInfo> item in this.channelTab)
5518 channelMask[item.Key] = item.Value.Keywords;
5521 return channelMask;
5524 #endif
5525 public void StartEvent(string eventName, EventAttribute eventAttribute)
5527 Debug.Assert(numParams == 0);
5528 Debug.Assert(this.eventName == null);
5529 this.eventName = eventName;
5530 numParams = 0;
5531 byteArrArgIndices = null;
5533 events.Append(" <event").
5534 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
5535 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
5536 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
5537 Append(" symbol=\"").Append(eventName).Append("\"");
5539 // at this point we add to the manifest's stringTab a message that is as-of-yet
5540 // "untranslated to manifest convention", b/c we don't have the number or position
5541 // of any byte[] args (which require string format index updates)
5542 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
5544 if (eventAttribute.Keywords != 0)
5545 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
5546 if (eventAttribute.Opcode != 0)
5547 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
5548 if (eventAttribute.Task != 0)
5549 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
5550 #if FEATURE_MANAGED_ETW_CHANNELS
5551 if (eventAttribute.Channel != 0)
5553 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
5555 #endif
5558 public void AddEventParameter(Type type, string name)
5560 if (numParams == 0)
5561 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
5562 if (type == typeof(byte[]))
5564 // mark this index as "extraneous" (it has no parallel in the managed signature)
5565 // we use these values in TranslateToManifestConvention()
5566 if (byteArrArgIndices == null)
5567 byteArrArgIndices = new List<int>(4);
5568 byteArrArgIndices.Add(numParams);
5570 // add an extra field to the template representing the length of the binary blob
5571 numParams++;
5572 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
5574 numParams++;
5575 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
5576 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
5577 // as for 'byte[]' args (blob_arg_name + "Size")
5578 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
5580 // add "length" attribute to the "blob" field in the template (referencing the field added above)
5581 templates.Append(" length=\"").Append(name).Append("Size\"");
5583 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
5584 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(ulong) && Enum.GetUnderlyingType(type) != typeof(long))
5586 templates.Append(" map=\"").Append(type.Name).Append("\"");
5587 if (mapsTab == null)
5588 mapsTab = new Dictionary<string, Type>();
5589 if (!mapsTab.ContainsKey(type.Name))
5590 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
5593 templates.Append("/>").AppendLine();
5595 public void EndEvent()
5597 Debug.Assert(eventName != null);
5599 if (numParams > 0)
5601 templates.Append(" </template>").AppendLine();
5602 events.Append(" template=\"").Append(eventName).Append("Args\"");
5604 events.Append("/>").AppendLine();
5606 if (byteArrArgIndices != null)
5607 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
5609 // at this point we have all the information we need to translate the C# Message
5610 // to the manifest string we'll put in the stringTab
5611 if (stringTab.TryGetValue("event_" + eventName, out string? msg))
5613 msg = TranslateToManifestConvention(msg, eventName);
5614 stringTab["event_" + eventName] = msg;
5617 eventName = null;
5618 numParams = 0;
5619 byteArrArgIndices = null;
5622 #if FEATURE_MANAGED_ETW_CHANNELS
5623 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
5624 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
5625 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
5626 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
5627 // to channels by the OS infrastructure).
5628 // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
5629 // assumed that the keyword for that channel should be that bit.
5630 // otherwise we allocate a channel bit for the channel.
5631 // explicit channel bits are only used by WCF to mimic an existing manifest,
5632 // so we don't dont do error checking.
5633 public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
5635 // strip off any non-channel keywords, since we are only interested in channels here.
5636 channelKeyword &= ValidPredefinedChannelKeywords;
5637 if (channelTab == null)
5639 channelTab = new Dictionary<int, ChannelInfo>(4);
5642 if (channelTab.Count == MaxCountChannels)
5643 ManifestError(SR.EventSource_MaxChannelExceeded);
5645 ChannelInfo? info;
5646 if (!channelTab.TryGetValue((int)channel, out info))
5648 // If we were not given an explicit channel, allocate one.
5649 if (channelKeyword != 0)
5651 channelKeyword = nextChannelKeywordBit;
5652 nextChannelKeywordBit >>= 1;
5655 else
5657 channelKeyword = info.Keywords;
5660 return channelKeyword;
5662 #endif
5664 public byte[] CreateManifest()
5666 string str = CreateManifestString();
5667 return Encoding.UTF8.GetBytes(str);
5670 public IList<string> Errors => errors;
5672 /// <summary>
5673 /// When validating an event source it adds the error to the error collection.
5674 /// When not validating it throws an exception if runtimeCritical is "true".
5675 /// Otherwise the error is ignored.
5676 /// </summary>
5677 /// <param name="msg"></param>
5678 /// <param name="runtimeCritical"></param>
5679 public void ManifestError(string msg, bool runtimeCritical = false)
5681 if ((flags & EventManifestOptions.Strict) != 0)
5682 errors.Add(msg);
5683 else if (runtimeCritical)
5684 throw new ArgumentException(msg);
5687 private string CreateManifestString()
5690 #if FEATURE_MANAGED_ETW_CHANNELS
5691 // Write out the channels
5692 if (channelTab != null)
5694 sb.Append(" <channels>").AppendLine();
5695 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
5696 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
5697 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
5698 foreach (KeyValuePair<int, ChannelInfo> kvpair in sortedChannels)
5700 int channel = kvpair.Key;
5701 ChannelInfo channelInfo = kvpair.Value;
5703 string? channelType = null;
5704 string elementName = "channel";
5705 bool enabled = false;
5706 string? fullName = null;
5707 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5708 string? isolation = null;
5709 string? access = null;
5710 #endif
5711 if (channelInfo.Attribs != null)
5713 EventChannelAttribute attribs = channelInfo.Attribs;
5714 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
5715 channelType = attribs.EventChannelType.ToString();
5716 enabled = attribs.Enabled;
5717 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5718 if (attribs.ImportChannel != null)
5720 fullName = attribs.ImportChannel;
5721 elementName = "importChannel";
5723 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
5724 isolation = attribs.Isolation.ToString();
5725 access = attribs.Access;
5726 #endif
5728 if (fullName == null)
5729 fullName = providerName + "/" + channelInfo.Name;
5731 sb.Append(" <").Append(elementName);
5732 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
5733 sb.Append(" name=\"").Append(fullName).Append("\"");
5734 if (elementName == "channel") // not applicable to importChannels.
5736 Debug.Assert(channelInfo.Name != null);
5737 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
5738 sb.Append(" value=\"").Append(channel).Append("\"");
5739 if (channelType != null)
5740 sb.Append(" type=\"").Append(channelType).Append("\"");
5741 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
5742 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5743 if (access != null)
5744 sb.Append(" access=\"").Append(access).Append("\"");
5745 if (isolation != null)
5746 sb.Append(" isolation=\"").Append(isolation).Append("\"");
5747 #endif
5749 sb.Append("/>").AppendLine();
5751 sb.Append(" </channels>").AppendLine();
5753 #endif
5755 // Write out the tasks
5756 if (taskTab != null)
5759 sb.Append(" <tasks>").AppendLine();
5760 var sortedTasks = new List<int>(taskTab.Keys);
5761 sortedTasks.Sort();
5762 foreach (int task in sortedTasks)
5764 sb.Append(" <task");
5765 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
5766 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
5768 sb.Append(" </tasks>").AppendLine();
5771 // Write out the maps
5772 if (mapsTab != null)
5774 sb.Append(" <maps>").AppendLine();
5775 foreach (Type enumType in mapsTab.Values)
5777 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
5778 string mapKind = isbitmap ? "bitMap" : "valueMap";
5779 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
5781 // write out each enum value
5782 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
5783 bool anyValuesWritten = false;
5784 foreach (FieldInfo staticField in staticFields)
5786 object? constantValObj = staticField.GetRawConstantValue();
5788 if (constantValObj != null)
5790 ulong hexValue;
5791 if (constantValObj is ulong)
5792 hexValue = (ulong)constantValObj; // This is the only integer type that can't be represented by a long.
5793 else
5794 hexValue = (ulong) Convert.ToInt64(constantValObj); // Handles all integer types except ulong.
5796 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
5797 // TODO: Warn people about the dropping of values.
5798 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
5799 continue;
5800 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
5801 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
5802 sb.Append("/>").AppendLine();
5803 anyValuesWritten = true;
5807 // the OS requires that bitmaps and valuemaps have at least one value or it reject the whole manifest.
5808 // To avoid that put a 'None' entry if there are no other values.
5809 if (!anyValuesWritten)
5811 sb.Append(" <map value=\"0x0\"");
5812 WriteMessageAttrib(sb, "map", enumType.Name + "." + "None", "None");
5813 sb.Append("/>").AppendLine();
5815 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
5817 sb.Append(" </maps>").AppendLine();
5820 // Write out the opcodes
5821 sb.Append(" <opcodes>").AppendLine();
5822 var sortedOpcodes = new List<int>(opcodeTab.Keys);
5823 sortedOpcodes.Sort();
5824 foreach (int opcode in sortedOpcodes)
5826 sb.Append(" <opcode");
5827 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
5828 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
5830 sb.Append(" </opcodes>").AppendLine();
5832 // Write out the keywords
5833 if (keywordTab != null)
5835 sb.Append(" <keywords>").AppendLine();
5836 var sortedKeywords = new List<ulong>(keywordTab.Keys);
5837 sortedKeywords.Sort();
5838 foreach (ulong keyword in sortedKeywords)
5840 sb.Append(" <keyword");
5841 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
5842 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
5844 sb.Append(" </keywords>").AppendLine();
5847 sb.Append(" <events>").AppendLine();
5848 sb.Append(events);
5849 sb.Append(" </events>").AppendLine();
5851 sb.Append(" <templates>").AppendLine();
5852 if (templates.Length > 0)
5854 sb.Append(templates);
5856 else
5858 // Work around a cornercase ETW issue where a manifest with no templates causes
5859 // ETW events to not get sent to their associated channel.
5860 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
5862 sb.Append(" </templates>").AppendLine();
5864 sb.Append("</provider>").AppendLine();
5865 sb.Append("</events>").AppendLine();
5866 sb.Append("</instrumentation>").AppendLine();
5868 // Output the localization information.
5869 sb.Append("<localization>").AppendLine();
5871 List<CultureInfo> cultures = (resources != null && (flags & EventManifestOptions.AllCultures) != 0) ?
5872 GetSupportedCultures() :
5873 new List<CultureInfo>() { CultureInfo.CurrentUICulture };
5875 var sortedStrings = new string[stringTab.Keys.Count];
5876 stringTab.Keys.CopyTo(sortedStrings, 0);
5877 Array.Sort<string>(sortedStrings, 0, sortedStrings.Length);
5879 foreach (CultureInfo ci in cultures)
5881 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
5882 sb.Append(" <stringTable>").AppendLine();
5884 foreach (string stringKey in sortedStrings)
5886 string? val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
5887 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
5889 sb.Append(" </stringTable>").AppendLine();
5890 sb.Append(" </resources>").AppendLine();
5892 sb.Append("</localization>").AppendLine();
5893 sb.AppendLine("</instrumentationManifest>");
5894 return sb.ToString();
5897 #region private
5898 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
5900 stringBuilder.Append(" name=\"").Append(name).Append("\"");
5901 WriteMessageAttrib(sb, elementName, name, name);
5903 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string? value)
5905 string key = elementName + "_" + name;
5906 // See if the user wants things localized.
5907 if (resources != null)
5909 // resource fallback: strings in the neutral culture will take precedence over inline strings
5910 string? localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
5911 if (localizedString != null)
5912 value = localizedString;
5914 if (value == null)
5915 return;
5917 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
5919 if (stringTab.TryGetValue(key, out string? prevValue) && !prevValue.Equals(value))
5921 ManifestError(SR.Format(SR.EventSource_DuplicateStringKey, key), true);
5922 return;
5925 stringTab[key] = value;
5927 internal string? GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
5929 string? value = null;
5930 if (resources != null)
5932 string? localizedString = resources.GetString(key, ci);
5933 if (localizedString != null)
5935 value = localizedString;
5936 if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
5938 string evtName = key.Substring("event_".Length);
5939 value = TranslateToManifestConvention(value, evtName);
5943 if (etwFormat && value == null)
5944 stringTab.TryGetValue(key, out value);
5946 return value;
5949 /// <summary>
5950 /// There's no API to enumerate all languages an assembly is localized into, so instead
5951 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
5952 /// assembly
5953 /// </summary>
5954 /// <returns></returns>
5955 private static List<CultureInfo> GetSupportedCultures()
5957 var cultures = new List<CultureInfo>();
5959 if (!cultures.Contains(CultureInfo.CurrentUICulture))
5960 cultures.Insert(0, CultureInfo.CurrentUICulture);
5961 return cultures;
5964 private static string GetLevelName(EventLevel level)
5966 return (((int)level >= 16) ? "" : "win:") + level.ToString();
5969 #if FEATURE_MANAGED_ETW_CHANNELS
5970 private string? GetChannelName(EventChannel channel, string eventName, string? eventMessage)
5972 ChannelInfo? info = null;
5973 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
5975 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
5976 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
5978 // allow channels to be auto-defined. The well known ones get their well known names, and the
5979 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
5980 if (channelTab == null)
5981 channelTab = new Dictionary<int, ChannelInfo>(4);
5983 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
5984 if (EventChannel.Debug < channel)
5985 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
5987 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
5988 if (!channelTab.TryGetValue((int)channel, out info))
5989 ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
5991 // events that specify admin channels *must* have non-null "Message" attributes
5992 if (resources != null && eventMessage == null)
5993 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
5995 Debug.Assert(info!.Attribs != null);
5996 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
5997 ManifestError(SR.Format(SR.EventSource_EventWithAdminChannelMustHaveMessage, eventName, info.Name));
5998 return info.Name;
6000 #endif
6001 private string GetTaskName(EventTask task, string eventName)
6003 if (task == EventTask.None)
6004 return "";
6006 string? ret;
6007 if (taskTab == null)
6008 taskTab = new Dictionary<int, string>();
6009 if (!taskTab.TryGetValue((int)task, out ret))
6010 ret = taskTab[(int)task] = eventName;
6011 return ret;
6014 private string? GetOpcodeName(EventOpcode opcode, string eventName)
6016 switch (opcode)
6018 case EventOpcode.Info:
6019 return "win:Info";
6020 case EventOpcode.Start:
6021 return "win:Start";
6022 case EventOpcode.Stop:
6023 return "win:Stop";
6024 case EventOpcode.DataCollectionStart:
6025 return "win:DC_Start";
6026 case EventOpcode.DataCollectionStop:
6027 return "win:DC_Stop";
6028 case EventOpcode.Extension:
6029 return "win:Extension";
6030 case EventOpcode.Reply:
6031 return "win:Reply";
6032 case EventOpcode.Resume:
6033 return "win:Resume";
6034 case EventOpcode.Suspend:
6035 return "win:Suspend";
6036 case EventOpcode.Send:
6037 return "win:Send";
6038 case EventOpcode.Receive:
6039 return "win:Receive";
6042 string? ret;
6043 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6045 ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true);
6046 ret = null;
6049 return ret;
6052 private string GetKeywords(ulong keywords, string eventName)
6054 #if FEATURE_MANAGED_ETW_CHANNELS
6055 // ignore keywords associate with channels
6056 // See ValidPredefinedChannelKeywords def for more.
6057 keywords &= ~ValidPredefinedChannelKeywords;
6058 #endif
6060 string ret = "";
6061 for (ulong bit = 1; bit != 0; bit <<= 1)
6063 if ((keywords & bit) != 0)
6065 string? keyword = null;
6066 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6067 (bit >= (ulong)0x1000000000000))
6069 // do not report Windows reserved keywords in the manifest (this allows the code
6070 // to be resilient to potential renaming of these keywords)
6071 keyword = string.Empty;
6073 if (keyword == null)
6075 ManifestError(SR.Format(SR.EventSource_UndefinedKeyword, "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6076 keyword = string.Empty;
6078 if (ret.Length != 0 && keyword.Length != 0)
6079 ret += " ";
6080 ret += keyword;
6083 return ret;
6086 private string GetTypeName(Type type)
6088 if (type.IsEnum())
6090 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6091 string typeName = GetTypeName(fields[0].FieldType);
6092 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6095 switch (type.GetTypeCode())
6097 case TypeCode.Boolean:
6098 return "win:Boolean";
6099 case TypeCode.Byte:
6100 return "win:UInt8";
6101 case TypeCode.Char:
6102 case TypeCode.UInt16:
6103 return "win:UInt16";
6104 case TypeCode.UInt32:
6105 return "win:UInt32";
6106 case TypeCode.UInt64:
6107 return "win:UInt64";
6108 case TypeCode.SByte:
6109 return "win:Int8";
6110 case TypeCode.Int16:
6111 return "win:Int16";
6112 case TypeCode.Int32:
6113 return "win:Int32";
6114 case TypeCode.Int64:
6115 return "win:Int64";
6116 case TypeCode.String:
6117 return "win:UnicodeString";
6118 case TypeCode.Single:
6119 return "win:Float";
6120 case TypeCode.Double:
6121 return "win:Double";
6122 case TypeCode.DateTime:
6123 return "win:FILETIME";
6124 default:
6125 if (type == typeof(Guid))
6126 return "win:GUID";
6127 else if (type == typeof(IntPtr))
6128 return "win:Pointer";
6129 else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6130 return "win:Binary";
6132 ManifestError(SR.Format(SR.EventSource_UnsupportedEventTypeInManifest, type.Name), true);
6133 return string.Empty;
6137 private static void UpdateStringBuilder([NotNull] ref StringBuilder? stringBuilder, string eventMessage, int startIndex, int count)
6139 if (stringBuilder == null)
6140 stringBuilder = new StringBuilder();
6141 stringBuilder.Append(eventMessage, startIndex, count);
6144 private static readonly string[] s_escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
6145 // Manifest messages use %N conventions for their message substitutions. Translate from
6146 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6147 private string TranslateToManifestConvention(string eventMessage, string evtName)
6149 StringBuilder? stringBuilder = null; // We lazily create this
6150 int writtenSoFar = 0;
6151 int chIdx = -1;
6152 for (int i = 0; ;)
6154 if (i >= eventMessage.Length)
6156 if (stringBuilder == null)
6157 return eventMessage;
6158 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6159 return stringBuilder.ToString();
6162 if (eventMessage[i] == '%')
6164 // handle format message escaping character '%' by escaping it
6165 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6166 stringBuilder.Append("%%");
6167 i++;
6168 writtenSoFar = i;
6170 else if (i < eventMessage.Length - 1 &&
6171 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6173 // handle C# escaped '{" and '}'
6174 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6175 stringBuilder.Append(eventMessage[i]);
6176 i++; i++;
6177 writtenSoFar = i;
6179 else if (eventMessage[i] == '{')
6181 int leftBracket = i;
6182 i++;
6183 int argNum = 0;
6184 while (i < eventMessage.Length && char.IsDigit(eventMessage[i]))
6186 argNum = argNum * 10 + eventMessage[i] - '0';
6187 i++;
6189 if (i < eventMessage.Length && eventMessage[i] == '}')
6191 i++;
6192 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6193 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6194 stringBuilder.Append('%').Append(manIndex);
6195 // An '!' after the insert specifier {n} will be interpreted as a literal.
6196 // We'll escape it so that mc.exe does not attempt to consider it the
6197 // beginning of a format string.
6198 if (i < eventMessage.Length && eventMessage[i] == '!')
6200 i++;
6201 stringBuilder.Append("%!");
6203 writtenSoFar = i;
6205 else
6207 ManifestError(SR.Format(SR.EventSource_UnsupportedMessageProperty, evtName, eventMessage));
6210 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6212 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6213 i++;
6214 stringBuilder.Append(s_escapes[chIdx]);
6215 writtenSoFar = i;
6217 else
6218 i++;
6222 private int TranslateIndexToManifestConvention(int idx, string evtName)
6224 List<int>? byteArrArgIndices;
6225 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6227 foreach (int byArrIdx in byteArrArgIndices)
6229 if (idx >= byArrIdx)
6230 ++idx;
6231 else
6232 break;
6235 return idx + 1;
6238 #if FEATURE_MANAGED_ETW_CHANNELS
6239 private class ChannelInfo
6241 public string? Name;
6242 public ulong Keywords;
6243 public EventChannelAttribute? Attribs;
6245 #endif
6247 private readonly Dictionary<int, string> opcodeTab;
6248 private Dictionary<int, string>? taskTab;
6249 #if FEATURE_MANAGED_ETW_CHANNELS
6250 private Dictionary<int, ChannelInfo>? channelTab;
6251 #endif
6252 private Dictionary<ulong, string>? keywordTab;
6253 private Dictionary<string, Type>? mapsTab;
6254 private readonly Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6256 #if FEATURE_MANAGED_ETW_CHANNELS
6257 // WCF used EventSource to mimic a existing ETW manifest. To support this
6258 // in just their case, we allowed them to specify the keywords associated
6259 // with their channels explicitly. ValidPredefinedChannelKeywords is
6260 // this set of channel keywords that we allow to be explicitly set. You
6261 // can ignore these bits otherwise.
6262 internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
6263 private ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
6264 private const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6265 #endif
6267 private readonly StringBuilder sb; // Holds the provider information.
6268 private readonly StringBuilder events; // Holds the events.
6269 private readonly StringBuilder templates;
6271 #if FEATURE_MANAGED_ETW_CHANNELS
6272 private readonly string providerName;
6273 #endif
6274 private readonly ResourceManager? resources; // Look up localized strings here.
6275 private readonly EventManifestOptions flags;
6276 private readonly IList<string> errors; // list of currently encountered errors
6277 private readonly Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6279 // State we track between StartEvent and EndEvent.
6280 private string? eventName; // Name of the event currently being processed.
6281 private int numParams; // keeps track of the number of args the event has.
6282 private List<int>? byteArrArgIndices; // keeps track of the index of each byte[] argument
6283 #endregion
6286 /// <summary>
6287 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6288 /// </summary>
6289 internal struct ManifestEnvelope
6291 public const int MaxChunkSize = 0xFF00;
6292 public enum ManifestFormats : byte
6294 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6297 #if FEATURE_MANAGED_ETW
6298 public ManifestFormats Format;
6299 public byte MajorVersion;
6300 public byte MinorVersion;
6301 public byte Magic;
6302 public ushort TotalChunks;
6303 public ushort ChunkNumber;
6304 #endif
6307 #endregion